├── LICENSE
├── README-fa.md
├── README.md
├── backup.py
├── host.py
├── main.py
├── pingtester.py
├── requirements.txt
└── update.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Cuf
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-fa.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | خوش اومدید به اسکریپت وی سی جی
10 |
11 |
12 |
13 | English
14 |
15 | /
16 |
17 | فارسی
18 |
19 |
20 |
21 |
22 |
23 | Easy To Generat With V2Ray Config Generator Easy Install With Few Clicks
24 |
25 |
26 | This Python script downloads free V2Ray configs , which are updated everyday and include
( Vmess & Vless & Trojan & ShadowSocks & ShadowSocksR )
27 | اینترنت برای همه ؛ یا هیچکس!
28 |
29 |
30 |
31 | 
32 | 
33 |
34 |
35 |
36 |
37 |
38 |

39 |
40 |
41 |
42 |
43 |
44 | # معرفی
45 |
46 | **اسکریپت وی سی جی پروژه ایی هست که از چندین URL سابسکرایبشن مختلف ، کانفیگ هایی را دریافت میکند و برای شما تعدادی کانفیگ تصادفی که نوع آن را از قبل مشخص کرده اید نمایش میدهد و شما میتونید آن کانفیگ را در یک فایل ذخیره یا QR کد برای آن ها بسازید.**
47 |
48 | **اگر فکر می کنید این پروژه برای شما مفید است ، ممنون میشم یک ستاره بدهید** :star2:
49 |
50 | **برای من قهوه بخر :**
51 |
52 | - Tron USDT (TRC20) : `TDZccmYTC8AwK5vxwgbc9qPQ4VZHMkFgY4`
53 |
54 | ### کانال تلگرام : [VCG Script](https://t.me/VCGScript)
55 |
56 |
57 |
58 | # امکانات
59 |
60 | - پشتیبانی از vless - vmess - trojan - ss - ssr
61 | - پشتیبانی از for - xtls - tls - reality - Grpc - ws - tcp
62 | - اعمال محدودیت در تعداد ساخت کانفیگ
63 | - امکان ذخیره کانفیگ ها و ساخت QR Code
64 | - تغییر لینک سابسکرایبشن به لینک دلخواه شما
65 | - تست پینگ از کانفیگ ها
66 | - متن باز و قابل ویرایش
67 | - چک کردن ریالیتی در ساب ها
68 | - پشتیبان گیری از x-ui
69 | - اپلود فایل ها در هاست
70 |
71 |
72 |
73 | # کلون و نصب اسکریپت
74 |
75 | نصب داشتن Python , Git
76 |
77 | ```
78 | git clone https://github.com/RealCuf/VCG-Script.git
79 | cd VCG-Script
80 | pip install -r requirements.txt
81 | python main.py
82 | ```
83 | > در C:\Users\System.name میتونید به سورس کد دسترسی داشته باشید
84 |
85 |
86 |
87 | # آشنایی با محیط
88 |
89 | جدول زیر رو مطالعه کنید!
90 | میتونید برای ساخت کانفیگ ها از دستور مورد نظر و در قسمت OPTIONS از توضیحات جدول / اسکریپت استفاده کنید
91 |
92 | |Number of configs|Vmess configs only|Vless configs only|Trojan configs only|Save configs to a file|Save QR codes|Reality Checker|Pingtester|x-ui Backup|Upload File
93 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
94 | |-n| -v| -l| -t| -s| -q| -e| -p| -b| -o|
95 |
96 | مثال :
97 |
98 | ````
99 | python main.py -n 10 -t -s -q
100 | ````
101 | > معنی دستور : 10 عدد کانفیگ تروجان به همراه ذخیره کانفیگ ها + ساخت QR Code
102 |
103 |
104 |
105 | ## تمام دستورات
106 |
107 |
108 | برای جزئیات دستورات کلیک کنید
109 |
110 |
111 |
112 | استفاده : `python main.py [Options]`
113 |
114 | | Command | Alternative command | Action |
115 | | :----: | ---------------------------------- | -------------------------------- |
116 | | `-n` | `--number` | Number of Configs - Default : 5 |
117 | | `-v` | `--vmess` | Vmess Configs only |
118 | | `-l` | `--vless` | Vless Configs only |
119 | | `-t` | `--trojan` | Trojan Configs only |
120 | | `-h` | `--shadowsocks` | ShadowSocks Configs only |
121 | | `-a` | `--shadowsocksr` | ShadowSocksR Configs only |
122 | | `-r` | `--reality` | Reality Checker |
123 | | `-s` | `--save` | Save Configs |
124 | | `-q` | `--qr` | Save QR codes |
125 | | `-b` | `--backup` | x-ui Backup |
126 | | `-p` | `--ping` | Pingtester |
127 | | `-o` | `--host` | Upload File to Host |
128 | | `-u` | `--update` | Update Script |
129 |
130 |
131 |
132 |
133 |
134 | ## چک کردن ریالیتی
135 |
136 |
137 | برای جزئیات ریالیتی کلیک کنید
138 |
139 |
140 |
141 | - میتوانید با افزودن یک دستور -r یا - -Reality ، کانفیگ هایی را که ریالیتی دارند ، استخراج کنید.
142 | ```
143 | python main.py -n 10 -l -r -s -q
144 | ```
145 | > معنی دستور : 10 عدد کانفیگ وی لس به همراه ذخیره کانفیگ ها + ساخت QR Code + ریالیتی
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | ## تست پینگ
154 |
155 |
156 | برای جزئیات پینگ کلیک کنید
157 |
158 |
159 |
160 | - برای پینگ یک فایل txt که شامل تعدادی کانفیگ است ، از دستور زیر استفاده کنید
161 | ```
162 | python main.py -p
163 | ```
164 | - سپس روی File Select کلیک کنید و در پوشه Conf ، فایل txt مورد نظر خود را انتخاب کنید
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | ## آپدیت اسکریپت
175 |
176 |
177 | برای جزئیات آپدیت کلیک کنید
178 |
179 |
180 |
181 | ```
182 | python main.py -u
183 | ```
184 | > قبل از بروزرسانی از پوشه های Database و QR Backup و QR بک آپ بگیرید
185 |
186 |
187 |
188 |
189 |
190 | ## بک اپ X-ui
191 |
192 |
193 | برای جزئیات X-ui کلیک کنید
194 |
195 |
196 |
197 | - برای تهیه نسخه پشتیبان از پنل از دستور زیر استفاده کنید
198 | ```
199 | python main.py -b
200 | ```
201 | - در بخش داده ها ، اطلاعات سرور خود را بنویسید
202 | > آیپی , پورت , یوزرنیم , پسورد , مسیر فایل
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | ## ویرایش کد
211 |
212 |
213 | برای جزئیات ویرایش کلیک کنید
214 |
215 |
216 |
217 | - در بخش های DECODED_URLS , ENCODED_URLS میتونید لینک سابسکرایبشن دلخواه خودتون رو قرار بدید!
218 |
219 | ```python
220 | # URLs for configs not encoded in a base64 string
221 | DECODED_URLS = [
222 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/sub_merge.txt",
223 | "https://raw.githubusercontent.com/awesome-vpn/awesome-vpn/master/all",
224 | "https://raw.githubusercontent.com/freefq/free/master/v2",
225 | "https://raw.fastgit.org/ripaojiedian/freenode/main/sub",
226 | ]
227 |
228 | # URLs for configs encoded in a base64 string
229 | ENCODED_URLS = [
230 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/vmess.txt",
231 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/vless.txt",
232 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/trojan.txt",
233 | ]
234 | ```
235 |
236 |
237 |
238 |
239 |
240 | # کتابخانه های استفاده شده در پروژه
241 |
242 | - Base64 - Datetime - OS - Random - Subprocess - Sys
243 | - Qrcode - Requests - Rich - Argparse - Time - Git
244 | - Tkinter - Ping3 - Threading - Pyperclip - Pysftp
245 | - PySimpleGUI - Ftplib - Webbrowser - Shutil
246 |
247 |
248 |
249 | # ارتباط با من
250 | ### حتماً به کانال بپیوندید و از من حمایت کنید
251 |
252 | 😶🌫️ Twitter : [CybrDriver](https://twitter.com/CybrDriver) -
253 | Channel : [Telegram](https://t.me/VCGScript)
254 |
255 | 
256 |
257 |
258 |
259 | # Stargazers over time
260 |
261 | [](https://starchart.cc/RealCuf/VCG-Script)
262 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Welcome to VCG Script
10 |
11 |
12 |
13 | English
14 |
15 | /
16 |
17 | فارسی
18 |
19 |
20 |
21 |
22 |
23 | Easy To Generat With V2Ray Config Generator Easy Install With Few Clicks
24 |
25 |
26 | This Python script downloads free V2Ray configs , which are updated everyday and include
( Vmess & Vless & Trojan & ShadowSocks & ShadowSocksR )
27 | اینترنت برای همه ؛ یا هیچکس!
28 |
29 |
30 |
31 | 
32 | 
33 |
34 |
35 |
36 |
37 |
38 |

39 |
40 |
41 |
42 |
43 |
44 | # Introduction
45 |
46 | **The VCG script is a project that receives the config from several different share URLs and displays some random config whose profile you specified earlier, and you can save that configuration in a file or create a QR code for them.**
47 |
48 | **If you think this project is helpful to you, you may wish to give a** :star2:
49 |
50 | **Buy Me a Coffee :**
51 |
52 | - Tron USDT (TRC20) : `TDZccmYTC8AwK5vxwgbc9qPQ4VZHMkFgY4`
53 |
54 | ### Telegram Channel : [VCG Script](https://t.me/VCGScript)
55 |
56 |
57 |
58 | # Features
59 |
60 | - Support vless - vmess - trojan - ss - ssr
61 | - Support for - xtls - tls - reality - Grpc - ws - tcp
62 | - Apply limits in the number of config
63 | - Save Configs & QR Code
64 | - Change the subs link
65 | - Pingtester
66 | - Open Source
67 | - Reality Checker
68 | - x-ui Backup
69 | - Upload File to Host
70 |
71 |
72 |
73 | # Clone and Install Script
74 |
75 | Installing Python , Git
76 |
77 | ```
78 | git clone https://github.com/RealCuf/VCG-Script.git
79 | cd VCG-Script
80 | pip install -r requirements.txt
81 | python main.py
82 | ```
83 | > In C:\Users\System.name you can access the Source Code
84 |
85 |
86 |
87 | # Familiarity with the environment
88 |
89 | Read the table below!
90 | You can use the desired command to create configs and in the OPTIONS section of the table / script description
91 |
92 | |Number of configs|Vmess configs only|Vless configs only|Trojan configs only|Save configs to a file|Save QR codes|Reality Checker|Pingtester|x-ui Backup|Upload File
93 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
94 | |-n| -v| -l| -t| -s| -q| -e| -p| -b| -o|
95 |
96 | Example :
97 |
98 | ````
99 | python main.py -n 10 -t -s -q
100 | ````
101 | > Command Meaning : 10 Trojan Configs with Config Save + QR Code Creation
102 |
103 |
104 |
105 | ## All Command
106 |
107 |
108 | Click for Command details
109 |
110 |
111 |
112 | Usage : `python main.py [Options]`
113 |
114 | | Command | Alternative command | Action |
115 | | :----: | ---------------------------------- | -------------------------------- |
116 | | `-n` | `--number` | Number of Configs - Default : 5 |
117 | | `-v` | `--vmess` | Vmess Configs only |
118 | | `-l` | `--vless` | Vless Configs only |
119 | | `-t` | `--trojan` | Trojan Configs only |
120 | | `-h` | `--shadowsocks` | ShadowSocks Configs only |
121 | | `-a` | `--shadowsocksr` | ShadowSocksR Configs only |
122 | | `-r` | `--reality` | Reality Checker |
123 | | `-s` | `--save` | Save Configs |
124 | | `-q` | `--qr` | Save QR codes |
125 | | `-b` | `--backup` | x-ui Backup |
126 | | `-p` | `--ping` | Pingtester |
127 | | `-o` | `--host` | Upload File to Host |
128 | | `-u` | `--update` | Update Script |
129 |
130 |
131 |
132 |
133 |
134 | ## Reality Checker
135 |
136 |
137 | Click for Reality details
138 |
139 |
140 |
141 | - You can extract the config that has a Reality by adding a -r or --reality command.
142 | ```
143 | python main.py -n 10 -l -r -s -q
144 | ```
145 | > Command Meaning : 10 vless Configs with Config Save + QR Code Creation + Reality
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | ## Ping Tester
154 |
155 |
156 | Click for Pingtester details
157 |
158 |
159 |
160 | - To ping a txt file that contains a number of config, use the following command
161 | ```
162 | python main.py -p
163 | ```
164 | - Then tap Select File and in the conf folder, select the txt file you want
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | ## Update Script
175 |
176 |
177 | Click for Update details
178 |
179 |
180 |
181 | ```
182 | python main.py -u
183 | ```
184 | > Backup conf and qr and database folders before updating
185 |
186 |
187 |
188 |
189 |
190 | ## X-ui Backup
191 |
192 |
193 | Click for XuiBackup details
194 |
195 |
196 |
197 | - Use the following command to back up the panel
198 | ```
199 | python main.py -b
200 | ```
201 | - In the data section, write your server information
202 | > ip , port , user , password , remote_path
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | ## Edit Source
211 |
212 |
213 | Click for Edit details
214 |
215 |
216 |
217 | - In the DECODED_URLS sections, ENCODED_URLS you can choose your favorite Subscribtion link!
218 |
219 | ```python
220 | # URLs for configs not encoded in a base64 string
221 | DECODED_URLS = [
222 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/sub_merge.txt",
223 | "https://raw.githubusercontent.com/awesome-vpn/awesome-vpn/master/all",
224 | "https://raw.githubusercontent.com/freefq/free/master/v2",
225 | "https://raw.fastgit.org/ripaojiedian/freenode/main/sub",
226 | ]
227 |
228 | # URLs for configs encoded in a base64 string
229 | ENCODED_URLS = [
230 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/vmess.txt",
231 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/vless.txt",
232 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/trojan.txt",
233 | ]
234 | ```
235 |
236 |
237 |
238 |
239 |
240 | # Libraries used in the project
241 |
242 | - Base64 - Datetime - OS - Random - Subprocess - Sys
243 | - Qrcode - Requests - Rich - Argparse - Time - Git
244 | - Tkinter - Ping3 - Threading - Pyperclip - Pysftp
245 | - PySimpleGUI - Ftplib - Webbrowser - Shutil
246 |
247 |
248 |
249 | # Contact Developer
250 | ### Be sure to join the channel and support us
251 |
252 | 😶🌫️ Twitter : [CybrDriver](https://twitter.com/CybrDriver) -
253 | Channel : [Telegram](https://t.me/VCGScript)
254 |
255 | 
256 |
257 |
258 |
259 | # Stargazers over time
260 | 
261 | [](https://starchart.cc/RealCuf/VCG-Script)
262 |
--------------------------------------------------------------------------------
/backup.py:
--------------------------------------------------------------------------------
1 | # GitHub : https://github.com/RealCuf
2 |
3 | import argparse
4 | import datetime
5 | import pysftp
6 | import os
7 | import sys
8 | from rich import print as rprint
9 |
10 | def parse_arguments():
11 | """Parse command line arguments."""
12 | parser = argparse.ArgumentParser()
13 | parser.add_argument("-b", "--backup", action="store_true")
14 | return parser.parse_args()
15 |
16 |
17 | def main():
18 | args = parse_arguments()
19 |
20 | if args.backup:
21 | # Prompt user for SFTP server information
22 | data = []
23 | while True:
24 | host = input("\n[ ] Enter S.Host (or 'q' to quit) :\n>>>> ")
25 | if host.lower() == 'q':
26 | break
27 | port = int(input("[ ] Enter S.Port:\n>>>> "))
28 | user = input("[ ] Enter S.Username:\n>>>> ")
29 | password = input("[ ] Enter S.Password:\n>>>> ")
30 | remote_path = input("[ ] Enter remote file path (Default : /etc/x-ui/x-ui.db):\n>>>> ")
31 | if not remote_path:
32 | remote_path = '/etc/x-ui/x-ui.db'
33 |
34 | data.append({
35 | "host": host,
36 | "port": port,
37 | "user": user,
38 | "pass": password,
39 | "remote": remote_path
40 | })
41 |
42 | # solar date
43 | date_str = datetime.datetime.now().strftime('%Y/%m/%d')
44 |
45 | # Work !!
46 | for i in data:
47 | # Information required to connect to the SFTP server
48 | hostname = i["host"]
49 | port = i["port"]
50 | username = i['user']
51 | password = i['pass']
52 | remote_path = i['remote']
53 |
54 | # Disable hostkey checking
55 | cnopts = pysftp.CnOpts()
56 | cnopts.hostkeys = None
57 |
58 | try:
59 | # Connect to the SFTP server
60 | with pysftp.Connection(host=hostname, username=username, password=password, port=port,
61 | cnopts=cnopts) as sftp:
62 | # Change directory to the root
63 | sftp.cwd('/')
64 |
65 | # Create the 'database' directory if it doesn't exist
66 | if not os.path.exists('database'):
67 | os.makedirs('database')
68 |
69 | # Generate a unique local file name based on timestamp
70 | timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
71 | local_filename = f'x-ui_{timestamp}.db'
72 |
73 | # Download the remote file
74 | sftp.get(remote_path, localpath=f'database/{local_filename}')
75 |
76 | rprint("[yellow]------------------------------------------------------------[/yellow]\n")
77 | rprint("[green]Downloaded successfully.[/green]")
78 | rprint(f"[green]Local file path[/green] : [magenta]database/{local_filename}[/magenta]")
79 | rprint("\n[cyan]You can find them in the 'database' directory.[/cyan] [magenta](type : start database)[/magenta]")
80 | rprint("\n[yellow]------------------------------------------------------------[/yellow]")
81 |
82 | os.system("start database")
83 |
84 |
85 | except pysftp.AuthenticationException:
86 | rprint("\n[red]Incorrect login credentials.[/red]")
87 | except pysftp.CredentialException:
88 | rprint("\n[red]Missing or invalid login credentials.[/red]")
89 | except pysftp.SSHException as e:
90 | rprint("\n[red]An SSH error occurred :[/red]", str(e))
91 | except pysftp.ConnectionException as e:
92 | rprint("\n[red]Could not connect to the server :[/red]", str(e))
93 | except Exception as e:
94 | rprint("\n[red]An error occurred :[/red]\n", str(e))
95 |
96 |
97 |
98 | if __name__ == '__main__':
99 | main()
100 |
101 |
102 | # GitHub : https://github.com/RealCuf
--------------------------------------------------------------------------------
/host.py:
--------------------------------------------------------------------------------
1 | import PySimpleGUI as sg
2 | import ftplib
3 | import webbrowser
4 | import argparse
5 |
6 |
7 | def parse_arguments():
8 | """Parse command line arguments."""
9 | parser = argparse.ArgumentParser()
10 | parser.add_argument("-o", "--host", action="store_true")
11 | return parser.parse_args()
12 |
13 | def upload_file_to_ftp(file_path, ftp_host, ftp_user, ftp_password, upload_path):
14 | try:
15 | ftp = ftplib.FTP(ftp_host)
16 | ftp.login(user=ftp_user, passwd=ftp_password)
17 |
18 | file = open(file_path, 'rb')
19 |
20 | ftp.storbinary('STOR {}/{}'.format(upload_path, file_path.split('/')[-1]), file)
21 |
22 | ftp.quit()
23 | file.close()
24 |
25 | return 'http://{}/{}'.format(ftp_host, file_path.split('/')[-1])
26 | except Exception as e:
27 | print('Error uploading file to FTP:', str(e))
28 | return None
29 |
30 | def open_default_browser(url):
31 | webbrowser.open(url)
32 |
33 | sg.theme('TealMono')
34 | font = ('Calibri', 11)
35 |
36 |
37 | layout = [
38 | [sg.Column([
39 | [sg.Text('IP/Domin Host:', font=font)],
40 | [sg.Text('Username:', font=font)],
41 | [sg.Text('Password:', font=font)],
42 | [sg.Text('Upload Path:', font=font)]
43 | ]),
44 | sg.Column([
45 | [sg.FileBrowse(key='-FILE-', font=font)],
46 | [sg.Input(key='-HOST-', size=(40, 1), font=font)],
47 | [sg.Input(key='-USER-', size=(20, 1), font=font)],
48 | [sg.Input(key='-PASS-', size=(20, 1), password_char='*', font=font)],
49 | [sg.Input(key='-PATH-', size=(40, 1), default_text='/home/(example)/public_html', font=font)],
50 | [sg.Button('Upload', key='-UPLOAD-', font=font), sg.Text('', key='-URL-', size=(20, 1), font=font)]
51 | ])]
52 | ]
53 |
54 |
55 | window = sg.Window('Upload File to Host', layout)
56 |
57 | while True:
58 | event, values = window.read()
59 | if event == sg.WINDOW_CLOSED:
60 | break
61 | elif event == '-UPLOAD-':
62 | file_path = values['-FILE-']
63 | ftp_host = values['-HOST-']
64 | ftp_user = values['-USER-']
65 | ftp_password = values['-PASS-']
66 | upload_path = values['-PATH-']
67 |
68 | result = upload_file_to_ftp(file_path, ftp_host, ftp_user, ftp_password, upload_path)
69 | if result:
70 | window['-URL-'].update('File link: {}'.format(result))
71 | open_default_browser(result)
72 | else:
73 | window['-URL-'].update('Error uploading file.')
74 |
75 | window.close()
76 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | # GitHub : https://github.com/RealCuf
2 |
3 | import base64
4 | import datetime
5 | import os
6 | import random
7 | import subprocess
8 | import sys
9 | import qrcode
10 | import requests
11 | from rich import print as rprint
12 | from rich.progress import track
13 | import argparse
14 | import time
15 |
16 | # URLs for configs not encoded in a base64 string
17 |
18 | DECODED_URLS = [
19 | "https://raw.githubusercontent.com/mostafasadeghifar/v2ray-config/main/config_file.txt",
20 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/Eternity.txt",
21 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/EternityAir.txt",
22 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/sub_merge.txt",
23 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/splitted/vmess.txt",
24 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/splitted/trojan.txt",
25 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/splitted/ssr.txt",
26 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/splitted/ss.txt",
27 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/vmess.txt",
28 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/trojan.txt",
29 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/ss.txt",
30 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/ssr.txt",
31 | "https://raw.githubusercontent.com/vpei/Free-Node-Merge/main/o/node.txt",
32 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub1.txt",
33 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub2.txt",
34 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub3.txt",
35 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub3.txt",
36 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub5.txt",
37 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub6.txt",
38 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub7.txt",
39 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Sub8.txt",
40 | ]
41 |
42 | # URLs for configs encoded in a base64 string
43 |
44 | ENCODED_URLS = [
45 | "https://raw.githubusercontent.com/wwwzywasia/free/main/sub",
46 | "https://raw.fastgit.org/wwwzywasia/free/main/sub",
47 | "https://gitlab.com/mfuu/v2ray/-/raw/master/v2ray",
48 | "https://raw.githubusercontent.com/mfuu/v2ray/master/v2ray",
49 | "https://raw.githubusercontent.com/Bardiafa/Free-V2ray-Config/main/Splitted-By-Protocol/vless.txt",
50 | "https://raw.githubusercontent.com/tbbatbb/Proxy/master/dist/v2ray.config.txt",
51 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/Eternity",
52 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/EternityAir",
53 | "https://raw.githubusercontent.com/ts-sf/fly/main/v2",
54 | "https://raw.fastgit.org/ts-sf/fly/main/v2",
55 | "https://raw.fgit.ml/ts-sf/fly/main/v2",
56 | "https://raw.githubusercontent.com/mahdibland/ShadowsocksAggregator/master/sub/sub_merge.txt",
57 | "https://raw.githubusercontent.com/tbbatbb/Proxy/master/dist/v2ray.config.txt",
58 | "https://raw.githubusercontent.com/awesome-vpn/awesome-vpn/master/all",
59 | "https://raw.githubusercontent.com/Pawdroid/Free-servers/main/sub",
60 | "https://raw.githubusercontent.com/AzadNetCH/Clash/main/V2Ray.txt",
61 | "https://raw.githubusercontent.com/aiboboxx/v2rayfree/main/v2",
62 | "https://raw.githubusercontent.com/freefq/free/master/v2",
63 | "https://raw.fastgit.org/ripaojiedian/freenode/main/sub",
64 | ]
65 |
66 | logo = r"""
67 | ____ _____________ ________
68 | \ \ / /\_ ___ \ / _____/
69 | \ Y / / \ \// \ ___
70 | \ / \ \___\ \_\ \
71 | \___/ \______ /\______ /
72 | \/ \/
73 | """
74 |
75 | COLORS = ["red", "green", "yellow", "blue", "magenta", "cyan", "white"]
76 | NOW = datetime.datetime.now()
77 | config_folder = "./conf"
78 | QR_DIR = "./qr"
79 |
80 |
81 | def check_reality(links):
82 | reality_links = []
83 |
84 | for link in links:
85 | response = requests.head(link)
86 | if response.status_code == 200:
87 | headers = response.headers
88 | if all(param in headers for param in ['Dest', 'ShortIds', 'security', 'tls', 'xtls']):
89 | reality_links.append(link)
90 | print(f"The link {link} is reachable and valid.")
91 | else:
92 | print(f"The link {link} is reachable but not reality.")
93 | else:
94 | print(f"The link {link} is not reachable or invalid.")
95 |
96 | if reality_links:
97 | print("Reality links found:")
98 | for link in reality_links:
99 | print(link)
100 |
101 | def main():
102 | parser = argparse.ArgumentParser()
103 | parser.add_argument("-e", "--reality", action="store_true", help="Check reality of URLs")
104 | args = parser.parse_args()
105 |
106 | if args.reality:
107 | all_urls = DECODED_URLS + ENCODED_URLS
108 | check_reality(all_urls)
109 |
110 | def get_config(url):
111 | """Get config from URL."""
112 |
113 | try:
114 | response = requests.get(url)
115 | if response.status_code == 200:
116 | return response.text
117 | except requests.exceptions.RequestException as e:
118 | print(f"Error: {e}")
119 | return None
120 |
121 | def decode_base64(string):
122 | """Decode base64 encoded string."""
123 |
124 | try:
125 | decoded_string = base64.b64decode(string).decode()
126 | return decoded_string
127 | except Exception as e:
128 | print(f"Error: {e}")
129 | return None
130 |
131 |
132 | def get_cleaned_configs(vmess=False, vless=False, trojan=False, shadowsocks=False, shadowsocksr=False):
133 | """Get cleaned configs."""
134 |
135 | configs = []
136 | all_urls = DECODED_URLS + ENCODED_URLS
137 |
138 | for url in all_urls:
139 | if url in DECODED_URLS:
140 | config = get_config(url)
141 | if config:
142 | configs.extend(config.splitlines())
143 | break
144 | elif url in ENCODED_URLS:
145 | decoded_config = decode_base64(get_config(url))
146 | if decoded_config:
147 | configs.extend(decoded_config.splitlines())
148 | break
149 |
150 | if not configs:
151 | return []
152 |
153 | if vmess:
154 | configs = [config for config in configs if "vmess" in config]
155 | elif vless:
156 | configs = [config for config in configs if "vless" in config]
157 | elif trojan:
158 | configs = [config for config in configs if "trojan" in config]
159 | elif shadowsocks:
160 | configs = [config for config in configs if "ss" in config]
161 | elif shadowsocksr:
162 | configs = [config for config in configs if "ssr" in config]
163 |
164 | return configs
165 |
166 |
167 | def save_configs(configs):
168 | """Save configs to file."""
169 |
170 | if not os.path.exists(config_folder):
171 | os.makedirs(config_folder)
172 |
173 | file_name = os.path.join(config_folder, f"configs_{NOW.strftime('%Y-%m-%d_%H-%M-%S')}.txt")
174 | with open(file_name, "w", encoding="utf-8") as f:
175 | f.write("\n".join(configs))
176 | rprint(f"[bold green]Config file saved to {file_name}[/bold green]")
177 |
178 |
179 | def get_random_color(word):
180 | """Returns a random color wrapped around the given word."""
181 |
182 | random_color = random.choice(COLORS)
183 | return f"[{random_color}]{word}[/{random_color}]"
184 |
185 |
186 | def get_random_config(configs, random_configs=5):
187 | """Returns a list of random configs from the given list of whole configs."""
188 |
189 | random_configs = random.sample(configs, random_configs)
190 | return random_configs
191 |
192 |
193 | def save_qr_code(data):
194 | """Save QR codes to qr_codes directory."""
195 |
196 | for index, qr_data in enumerate(data, start=1):
197 | qr = qrcode.QRCode(
198 | version=1,
199 | error_correction=qrcode.constants.ERROR_CORRECT_L,
200 | box_size=10,
201 | border=4,
202 | )
203 | qr.add_data(qr_data)
204 | qr.make(fit=True)
205 |
206 | if not os.path.exists(QR_DIR):
207 | os.makedirs(QR_DIR)
208 |
209 | img = qr.make_image(fill_color="white", back_color="black")
210 | file_name = f"{QR_DIR}/{str(index).zfill(0000)}_qr_code_{NOW.strftime('%Y-%m-%d_%H-%M-%S')}.png"
211 | img.save(file_name)
212 |
213 | rprint(
214 | f"[bold yellow]{len(data)} QR code(s) saved to qr_codes directory.[/bold yellow]"
215 | )
216 |
217 |
218 | if "-p" in sys.argv or "--ping" in sys.argv:
219 | subprocess.call(["python", "pingtester.py"])
220 | os.system("python main.py")
221 | sys.exit()
222 |
223 |
224 | import sys
225 |
226 | if '-u' in sys.argv or '--update' in sys.argv:
227 | with open('update.py', 'r') as file:
228 | code = compile(file.read(), 'update.py', 'exec')
229 | exec(code)
230 | time.sleep(2)
231 | os.system("python main.py")
232 | sys.exit()
233 |
234 | if '-b' in sys.argv or '--backup' in sys.argv:
235 | with open('backup.py', 'r') as file:
236 | code = compile(file.read(), 'backup.py', 'exec')
237 | exec(code)
238 | time.sleep(2)
239 | os.system("python main.py")
240 | sys.exit()
241 |
242 | if '-o' in sys.argv or '--host' in sys.argv:
243 | with open('host.py', 'r') as file:
244 | code = compile(file.read(), 'host.py', 'exec')
245 | exec(code)
246 | os.system("python main.py")
247 | sys.exit()
248 |
249 | def run_code():
250 |
251 | if len(sys.argv) == 1:
252 | rprint("[magenta]------------------------------------------------------------▶[/magenta]")
253 | rprint(logo)
254 | rprint("\n[cyan]V E R S I O N : [cyan][[/cyan][yellow]v 1.6.4[yellow][cyan]][/cyan] [bold green]GitHub : [cyan]https://github.com/RealCuf[/cyan][/bold green]\n")
255 | rprint("[cyan]U S A G E : [cyan][white]python main.py[/white] [cyan][[/cyan][bold yellow]O P T I O N S[/bold yellow][cyan]][/cyan]\n")
256 | rprint("[bold yellow]O P T I O N S : [/bold yellow]\n")
257 | rprint("• [bold yellow]-n[/bold yellow][bold blue] --number[/bold blue] 〔 [bold green]Number of Configs - Default : 5 [/bold green]")
258 | rprint("• [bold yellow]-v[/bold yellow][bold blue] --vmess [/bold blue] 〔 [bold green]Vmess Configs only[/bold green]")
259 | rprint("• [bold yellow]-l[/bold yellow][bold blue] --vless [/bold blue] 〔 [bold green]Vless Configs only[/bold green]")
260 | rprint("• [bold yellow]-t[/bold yellow][bold blue] --trojan[/bold blue] 〔 [bold green]Trojan Configs only[/bold green]")
261 | rprint("• [bold yellow]-h[/bold yellow][bold blue] --shadowsocks[/bold blue] 〔 [bold green]ShadowSocks Configs only[/bold green]")
262 | rprint("• [bold yellow]-a[/bold yellow][bold blue] --shadowsocksr[/bold blue] 〔 [bold green]ShadowSocksR Configs only[/bold green]")
263 | rprint("• [bold yellow]-r[/bold yellow][bold blue] --reality [/bold blue] 〔 [bold green]Reality Checker[/bold green]")
264 | rprint("• [bold yellow]-s[/bold yellow][bold blue] --save [/bold blue] 〔 [bold green]Save Configs[/bold green]")
265 | rprint("• [bold yellow]-q[/bold yellow][bold blue] --qr [/bold blue] 〔 [bold green]Save QR codes[/bold green]")
266 | rprint("• [bold yellow]-b[/bold yellow][bold blue] --backup [/bold blue] 〔 [bold green]x-ui Backup[/bold green]")
267 | rprint("• [bold yellow]-p[/bold yellow][bold blue] --ping [/bold blue] 〔 [bold green]Pingtester[/bold green]")
268 | rprint("• [bold yellow]-o[/bold yellow][bold blue] --host [/bold blue] 〔 [bold green]Upload File to Host[/bold green]")
269 | rprint("• [bold yellow]-u[/bold yellow][bold blue] --update[/bold blue] 〔 [bold green]Update Script[/bold green]\n")
270 | rprint("[magenta]------------------------------------------------------------▶[/magenta]")
271 | sys.exit(1)
272 |
273 | if "-v" in sys.argv or "--vmess" in sys.argv:
274 | configs = get_cleaned_configs(vmess=True)
275 | elif "-l" in sys.argv or "--vless" in sys.argv:
276 | configs = get_cleaned_configs(vless=True)
277 | elif "-t" in sys.argv or "--trojan" in sys.argv:
278 | configs = get_cleaned_configs(trojan=True)
279 | elif "-h" in sys.argv or "--shadowsocks" in sys.argv:
280 | configs = get_cleaned_configs(shadowsocks=True)
281 | elif "-a" in sys.argv or "--shadowsocksr" in sys.argv:
282 | configs = get_cleaned_configs(shadowsocksr=True)
283 | else:
284 | configs = get_cleaned_configs()
285 |
286 | configs_length = len(configs)
287 | rprint(f"\n[bold yellow]{configs_length} configs downloaded.[/bold yellow]")
288 |
289 | if configs_length == 0:
290 | rprint("[bold red]No configs found.[/bold red]\n")
291 | sys.exit(1)
292 |
293 | if "-n" in sys.argv or "--number" in sys.argv:
294 | try:
295 | config_number = int(sys.argv[sys.argv.index("-n") + 1])
296 | except ValueError:
297 | try:
298 | config_number = int(sys.argv[sys.argv.index("--number") + 1])
299 | except ValueError:
300 | config_number = 5
301 | if config_number > configs_length:
302 | config_number = configs_length
303 |
304 | random_configs = get_random_config(configs, config_number)
305 |
306 | if "--silent" not in sys.argv:
307 | for config in random_configs:
308 | rprint(get_random_color(config))
309 | rprint("")
310 |
311 | if "-s" in sys.argv or "--save" in sys.argv:
312 | save_configs(random_configs)
313 |
314 | if "-q" in sys.argv or "--qr" in sys.argv:
315 | save_qr_code(random_configs)
316 |
317 | rprint(f"[bold yellow]{config_number} random configs generated.[/bold yellow]\n")
318 |
319 | os.system("start conf")
320 | os.system("start qr")
321 |
322 | time.sleep(3)
323 | os.system("python main.py")
324 |
325 |
326 | if __name__ == "__main__":
327 | run_code()
328 |
329 | # GitHub : https://github.com/RealCuf
330 |
--------------------------------------------------------------------------------
/pingtester.py:
--------------------------------------------------------------------------------
1 | # GitHub : https://github.com/RealCuf
2 |
3 | import tkinter as tk
4 | from tkinter import ttk
5 | from tkinter import filedialog
6 | from ping3 import ping
7 | import json
8 | import base64
9 | import threading
10 | import pyperclip
11 |
12 |
13 | proxies = []
14 | current_file = ""
15 |
16 |
17 | def extract_proxy_info(filepath):
18 | with open(filepath, 'r') as file:
19 | lines = file.readlines()
20 | proxies.clear()
21 | for line in lines:
22 | line = line.strip()
23 | if line.startswith('vmess://'):
24 | config_type = 'Vmess'
25 | proxy_info = line[8:]
26 | try:
27 | proxy_info = base64.urlsafe_b64decode(proxy_info + '=' * (-len(proxy_info) % 4)).decode('utf-8')
28 | except Exception as e:
29 | print(f'Error decoding Vmess proxy info: {str(e)}')
30 | continue
31 | elif line.startswith('vless://'):
32 | config_type = 'Vless'
33 | proxy_info = line[8:]
34 | elif line.startswith('trojan://'):
35 | config_type = 'Trojan'
36 | proxy_info = line[9:]
37 | elif line.startswith('ss://'):
38 | config_type = 'SS'
39 | proxy_info = line[9:]
40 | elif line.startswith('ssr://'):
41 | config_type = 'SSR'
42 | proxy_info = line[9:]
43 | else:
44 | continue
45 |
46 | proxy_info_parts = proxy_info.split('@')
47 | if len(proxy_info_parts) == 2:
48 | address_port = proxy_info_parts[1].split('#')[0]
49 | address_parts = address_port.split(':')
50 | if len(address_parts) == 2:
51 | address = address_parts[0]
52 | port = address_parts[1]
53 | proxies.append((config_type, address, port, line))
54 | elif config_type == 'Vmess':
55 | proxy_info_dict = json.loads(proxy_info)
56 | proxies.append((config_type, proxy_info_dict.get('add'), proxy_info_dict.get('port'), line))
57 |
58 |
59 | def select_file():
60 | filepath = filedialog.askopenfilename(filetypes=[('Text Files', '*.txt')])
61 | if filepath:
62 | ping_proxy(filepath)
63 |
64 |
65 | def ping_proxy(filepath=None):
66 | if filepath:
67 | extract_proxy_info(filepath)
68 | global current_file
69 | current_file = filepath
70 |
71 | root = tk.Tk()
72 | root.title('Ping Results v2.3.1')
73 |
74 | style = ttk.Style()
75 | style.theme_use('clam')
76 |
77 | refresh_button = ttk.Button(root, text='Refresh', command=lambda: refresh_results(root))
78 | refresh_button.pack(pady=10)
79 |
80 | result_frame = ttk.Frame(root)
81 | result_frame.pack(fill=tk.BOTH, expand=True)
82 |
83 | for index, proxy in enumerate(proxies):
84 | config_type, address, port, proxy_info = proxy
85 |
86 | result_label = ttk.Label(result_frame, text=f'{config_type} Config (Domain or IP: {address}): ')
87 | result_label.grid(row=index, column=0, sticky='w', padx=10, pady=5)
88 |
89 | response_label = ttk.Label(result_frame, text="Pinging...")
90 | response_label.grid(row=index, column=1, padx=10, pady=5)
91 |
92 | copy_button = ttk.Button(result_frame, text="Copy", command=lambda txt=proxy_info: copy_to_clipboard(txt))
93 | copy_button.grid(row=index, column=2, padx=10, pady=5)
94 |
95 | threading.Thread(target=ping_proxy_async, args=(address, response_label)).start()
96 |
97 | root.update_idletasks()
98 | root.mainloop()
99 |
100 |
101 | for proxy in proxies:
102 | config_type, address, port, proxy_info = proxy
103 | result = f'{config_type} Config (Domain or IP: {address}): '
104 |
105 | label = ttk.Label(result_frame, text=result)
106 | label.pack()
107 |
108 | response_label = ttk.Label(result_frame, text="Pinging...")
109 | response_label.pack()
110 |
111 | copy_button = ttk.Button(result_frame, text="Copy", command=lambda txt=proxy_info: copy_to_clipboard(txt))
112 | copy_button.pack()
113 |
114 | threading.Thread(target=ping_proxy_async, args=(address, response_label)).start()
115 |
116 | root.geometry('500x430')
117 | root.mainloop()
118 |
119 |
120 | def ping_proxy_async(address, response_label):
121 | try:
122 | response_time = ping(address, timeout=1)
123 | if response_time is not None:
124 | response_time_str = '{:.0f} ms'.format(response_time * 1000)
125 | if response_time >= 100:
126 | response_time_str = '\033[91m{}\033[0m'.format(response_time_str, foreground='red')
127 | else:
128 | response_label.config(text=response_time_str, foreground='green')
129 | else:
130 | response_label.config(text="failed", foreground='red')
131 | except Exception as e:
132 | response_label.config(text="Error: {}".format(str(e)), foreground='red')
133 |
134 |
135 | def refresh_results(root):
136 | root.destroy()
137 | ping_proxy(current_file)
138 |
139 | def copy_to_clipboard(txt):
140 | pyperclip.copy(txt)
141 |
142 | def create_gui():
143 | root = tk.Tk()
144 | root.title('VCG Pinger v2.3.1')
145 |
146 | style = ttk.Style()
147 | style.theme_use('clam')
148 |
149 | screen_width = root.winfo_screenwidth()
150 | screen_height = root.winfo_screenheight()
151 |
152 | window_width = 300
153 | window_height = 55
154 | x = (screen_width - window_width) // 2
155 | y = (screen_height - window_height) // 2
156 |
157 | root.geometry(f'{window_width}x{window_height}+{x}+{y}')
158 | root.resizable(False, False)
159 |
160 | select_button = ttk.Button(root, text='Select File', command=select_file)
161 | select_button.pack(pady=10)
162 |
163 | root.mainloop()
164 |
165 |
166 | if __name__ == "__main__":
167 | create_gui()
168 |
169 | # GitHub : https://github.com/RealCuf
170 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | PyQRCode
2 | qrcode
3 | requests
4 | rich
5 | pysftp
6 | PySimpleGUI
7 | ping3
8 | pyperclip
9 | argparse
10 | importlib
11 |
12 | # pip install -r requirements.txt
13 |
--------------------------------------------------------------------------------
/update.py:
--------------------------------------------------------------------------------
1 | # GitHub : https://github.com/RealCuf
2 |
3 | import os
4 | import argparse
5 | from git import Repo
6 | import shutil
7 | from rich import print as rprint
8 |
9 | COLORS = ["red", "green", "yellow", "blue", "magenta", "cyan", "white"]
10 |
11 | def update_project(repo_url, project_dir):
12 | # Remove the previous project directory
13 | if os.path.exists(project_dir):
14 | rprint("\n[bold yellow][ ][/bold yellow] [bold white]Removing the previous project...[/bold white]")
15 | shutil.rmtree(project_dir, onerror=onerror)
16 |
17 | # Clone the new repository
18 | rprint("\n[bold yellow][ ][/bold yellow] [bold white]Cloning the new repository...[/bold white]")
19 | Repo.clone_from(repo_url, project_dir)
20 |
21 | rprint("\n[bold green][ ][/bold green] [bold white]Project successfully updated.[/bold white]\n")
22 |
23 | def onerror(func, path, exc_info):
24 | """
25 | Error handler for `shutil.rmtree`.
26 | If the error is due to a permission issue (Access is denied),
27 | it will attempt to change the file permissions and retry the operation.
28 | """
29 | import stat
30 | import errno
31 |
32 | # Get the exception details
33 | exception_class, exception, traceback = exc_info
34 |
35 | # Check if the error is due to a permission issue (Access is denied)
36 | if exception.errno == errno.EACCES:
37 | # Change the file permissions to allow removal
38 | os.chmod(path, stat.S_IWRITE)
39 |
40 | # Retry the operation
41 | func(path)
42 | else:
43 | # Re-raise the exception for other errors
44 | raise exception
45 |
46 | def main():
47 | parser = argparse.ArgumentParser(description='Update project')
48 | parser.add_argument('-u', '--update', action='store_true', help='Update the project')
49 |
50 | args = parser.parse_args()
51 |
52 | if args.update:
53 | # New repository information
54 | repo_url = "https://github.com/RealCuf/VCG-Script.git" # New repo URL
55 |
56 | # Get the current user's home directory
57 | home_dir = os.path.expanduser("~")
58 |
59 | # Set the project directory
60 | project_dir = os.path.join(home_dir, "VCG-Script")
61 |
62 | # Update the project
63 | update_project(repo_url, project_dir)
64 |
65 | os.system("python main.py")
66 |
67 | if __name__ == '__main__':
68 | main()
69 |
70 | # GitHub : https://github.com/RealCuf
--------------------------------------------------------------------------------