imseldrith commited on
Commit
9934299
·
1 Parent(s): 33a1a42

Upload 6 files

Browse files
Uploader/functions/__init__.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
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
+
23
+ from .display_progress import *
24
+ from .help_Nekmo_ffmpeg import *
25
+ from .help_uploadbot import *
26
+ from .help_ytdl import *
27
+ from .ran_text import *
Uploader/functions/display_progress.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
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
+
23
+ import math
24
+ import time
25
+ import logging
26
+
27
+ logging.basicConfig(level=logging.DEBUG,
28
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
29
+ logger = logging.getLogger(__name__)
30
+ logging.getLogger("pyrogram").setLevel(logging.WARNING)
31
+
32
+
33
+ async def progress_for_pyrogram(
34
+ current,
35
+ total,
36
+ ud_type,
37
+ message,
38
+ start
39
+ ):
40
+ now = time.time()
41
+ diff = now - start
42
+ if round(diff % 10.00) == 0 or current == total:
43
+ # if round(current / total * 100, 0) % 5 == 0:
44
+ percentage = current * 100 / total
45
+ speed = current / diff
46
+ elapsed_time = round(diff) * 1000
47
+ time_to_completion = round((total - current) / speed) * 1000
48
+ estimated_total_time = elapsed_time + time_to_completion
49
+
50
+ elapsed_time = TimeFormatter(milliseconds=elapsed_time)
51
+ estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
52
+
53
+ progress = "[{0}{1}] \nP: {2}%\n".format(
54
+ ''.join(["◾" for _ in range(math.floor(percentage / 5))]),
55
+ ''.join(["◽" for _ in range(20 - math.floor(percentage / 5))]),
56
+ round(percentage, 2),
57
+ )
58
+
59
+ tmp = progress + "{0} of {1}\n\nSpeed: {2}/s\n\nETA: {3}\n\n".format(
60
+ humanbytes(current),
61
+ humanbytes(total),
62
+ humanbytes(speed),
63
+ # elapsed_time if elapsed_time != '' else "0 s",
64
+ estimated_total_time if estimated_total_time != '' else "0 s"
65
+ )
66
+ try:
67
+ await message.edit(text=f"{ud_type}\n {tmp}")
68
+ except Exception as e:
69
+ logger.info(f"Error {e}")
70
+ return
71
+
72
+
73
+ SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
74
+
75
+
76
+ def huanbytes(size_in_bytes) -> str:
77
+ if size_in_bytes is None:
78
+ return '0B'
79
+ index = 0
80
+ while size_in_bytes >= 1024:
81
+ size_in_bytes /= 1024
82
+ index += 1
83
+ try:
84
+ return f'{round(size_in_bytes, 2)}{SIZE_UNITS[index]}'
85
+ except IndexError:
86
+ return 'File too large'
87
+
88
+
89
+ def humanbytes(size):
90
+ # https://stackoverflow.com/a/49361727/4723940
91
+ # 2**10 = 1024
92
+ if not size:
93
+ return ""
94
+ power = 2**10
95
+ n = 0
96
+ Dic_powerN = {0: ' ', 1: 'K', 2: 'M', 3: 'G', 4: 'T'}
97
+ while size > power:
98
+ size /= power
99
+ n += 1
100
+ return f"{str(round(size, 2))} {Dic_powerN[n]}B"
101
+
102
+
103
+ def TimeFormatter(milliseconds: int) -> str:
104
+ seconds, milliseconds = divmod(milliseconds, 1000)
105
+ minutes, seconds = divmod(seconds, 60)
106
+ hours, minutes = divmod(minutes, 60)
107
+ days, hours = divmod(hours, 24)
108
+ tmp = (
109
+ (f"{str(days)}d, " if days else "")
110
+ + (f"{str(hours)}h, " if hours else "")
111
+ + (f"{str(minutes)}m, " if minutes else "")
112
+ + (f"{str(seconds)}s, " if seconds else "")
113
+ + (f"{str(milliseconds)}ms, " if milliseconds else "")
114
+ )
115
+
116
+ return tmp[:-2]
Uploader/functions/help_Nekmo_ffmpeg.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
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
+
23
+ import os
24
+ import asyncio
25
+ from hachoir.parser import createParser
26
+ from hachoir.metadata import extractMetadata
27
+ import time
28
+ import logging
29
+ logging.basicConfig(level=logging.DEBUG,
30
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ async def place_water_mark(input_file, output_file, water_mark_file):
35
+ watermarked_file = f"{output_file}.watermark.png"
36
+ metadata = extractMetadata(createParser(input_file))
37
+ width = metadata.get("width")
38
+ # https://stackoverflow.com/a/34547184/4723940
39
+ shrink_watermark_file_genertor_command = [
40
+ "ffmpeg",
41
+ "-i",
42
+ water_mark_file,
43
+ "-y -v quiet",
44
+ "-vf",
45
+ f"scale={width}*0.5:-1",
46
+ watermarked_file,
47
+ ]
48
+
49
+ # print(shrink_watermark_file_genertor_command)
50
+ process = await asyncio.create_subprocess_exec(
51
+ *shrink_watermark_file_genertor_command,
52
+ # stdout must a pipe to be accessible as process.stdout
53
+ stdout=asyncio.subprocess.PIPE,
54
+ stderr=asyncio.subprocess.PIPE,
55
+ )
56
+ # Wait for the subprocess to finish
57
+ stdout, stderr = await process.communicate()
58
+ e_response = stderr.decode().strip()
59
+ t_response = stdout.decode().strip()
60
+ commands_to_execute = [
61
+ "ffmpeg",
62
+ "-i", input_file,
63
+ "-i", watermarked_file,
64
+ "-filter_complex",
65
+ # https://stackoverflow.com/a/16235519
66
+ # "\"[0:0] scale=400:225 [wm]; [wm][1:0] overlay=305:0 [out]\"",
67
+ # "-map \"[out]\" -b:v 896k -r 20 -an ",
68
+ "\"overlay=(main_w-overlay_w):(main_h-overlay_h)\"",
69
+ # "-vf \"drawtext=text='@FFMovingPictureExpertGroupBOT':x=W-(W/2):y=H-(H/2):fontfile=" + Config.FONT_FILE + ":fontsize=12:fontcolor=white:shadowcolor=black:shadowx=5:shadowy=5\"",
70
+ output_file
71
+ ]
72
+ # print(commands_to_execute)
73
+ process = await asyncio.create_subprocess_exec(
74
+ *commands_to_execute,
75
+ # stdout must a pipe to be accessible as process.stdout
76
+ stdout=asyncio.subprocess.PIPE,
77
+ stderr=asyncio.subprocess.PIPE,
78
+ )
79
+ # Wait for the subprocess to finish
80
+ stdout, stderr = await process.communicate()
81
+ e_response = stderr.decode().strip()
82
+ t_response = stdout.decode().strip()
83
+ return output_file
84
+
85
+
86
+ async def take_screen_shot(video_file, output_directory, ttl):
87
+ # https://stackoverflow.com/a/13891070/4723940
88
+ out_put_file_name = output_directory + \
89
+ "/" + str(time.time()) + ".jpg"
90
+ file_genertor_command = [
91
+ "ffmpeg",
92
+ "-ss",
93
+ str(ttl),
94
+ "-i",
95
+ video_file,
96
+ "-vframes",
97
+ "1",
98
+ out_put_file_name
99
+ ]
100
+ # width = "90"
101
+ process = await asyncio.create_subprocess_exec(
102
+ *file_genertor_command,
103
+ # stdout must a pipe to be accessible as process.stdout
104
+ stdout=asyncio.subprocess.PIPE,
105
+ stderr=asyncio.subprocess.PIPE,
106
+ )
107
+ # Wait for the subprocess to finish
108
+ stdout, stderr = await process.communicate()
109
+ e_response = stderr.decode().strip()
110
+ t_response = stdout.decode().strip()
111
+ return out_put_file_name if os.path.lexists(out_put_file_name) else None
112
+
113
+ # https://github.com/Nekmo/telegram-upload/blob/master/telegram_upload/video.py#L26
114
+
115
+
116
+ async def cult_small_video(video_file, output_directory, start_time, end_time):
117
+ # https://stackoverflow.com/a/13891070/4723940
118
+ out_put_file_name = output_directory + \
119
+ "/" + str(round(time.time())) + ".mp4"
120
+ file_genertor_command = [
121
+ "ffmpeg",
122
+ "-i",
123
+ video_file,
124
+ "-ss",
125
+ start_time,
126
+ "-to",
127
+ end_time,
128
+ "-async",
129
+ "1",
130
+ "-strict",
131
+ "-2",
132
+ out_put_file_name
133
+ ]
134
+ process = await asyncio.create_subprocess_exec(
135
+ *file_genertor_command,
136
+ # stdout must a pipe to be accessible as process.stdout
137
+ stdout=asyncio.subprocess.PIPE,
138
+ stderr=asyncio.subprocess.PIPE,
139
+ )
140
+ # Wait for the subprocess to finish
141
+ stdout, stderr = await process.communicate()
142
+ e_response = stderr.decode().strip()
143
+ t_response = stdout.decode().strip()
144
+ return out_put_file_name if os.path.lexists(out_put_file_name) else None
145
+
146
+
147
+ async def generate_screen_shots(
148
+ video_file,
149
+ output_directory,
150
+ is_watermarkable,
151
+ wf,
152
+ min_duration,
153
+ no_of_photos
154
+ ):
155
+ metadata = extractMetadata(createParser(video_file))
156
+ duration = 0
157
+ if metadata is not None and metadata.has("duration"):
158
+ duration = metadata.get('duration').seconds
159
+ if duration > min_duration:
160
+ images = []
161
+ ttl_step = duration // no_of_photos
162
+ current_ttl = ttl_step
163
+ for _ in range(no_of_photos):
164
+ ss_img = await take_screen_shot(video_file, output_directory, current_ttl)
165
+ current_ttl = current_ttl + ttl_step
166
+ if is_watermarkable:
167
+ ss_img = await place_water_mark(
168
+ ss_img, f"{output_directory}/{str(time.time())}.jpg", wf
169
+ )
170
+
171
+ images.append(ss_img)
172
+ return images
173
+ else:
174
+ return None
Uploader/functions/help_uploadbot.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
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
+
23
+ import os
24
+ import time
25
+ import requests
26
+ import logging
27
+
28
+ from Uploader.functions.display_progress import humanbytes
29
+
30
+ logging.basicConfig(level=logging.DEBUG,
31
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ def DetectFileSize(url):
36
+ r = requests.get(url, allow_redirects=True, stream=True)
37
+ return int(r.headers.get("content-length", 0))
38
+
39
+
40
+ def DownLoadFile(url, file_name, chunk_size, client, ud_type, message_id, chat_id):
41
+ if os.path.exists(file_name):
42
+ os.remove(file_name)
43
+ if not url:
44
+ return file_name
45
+ r = requests.get(url, allow_redirects=True, stream=True)
46
+ # https://stackoverflow.com/a/47342052/4723940
47
+ total_size = int(r.headers.get("content-length", 0))
48
+ downloaded_size = 0
49
+ with open(file_name, 'wb') as fd:
50
+ for chunk in r.iter_content(chunk_size=chunk_size):
51
+ if chunk:
52
+ fd.write(chunk)
53
+ downloaded_size += chunk_size
54
+ if (
55
+ client is not None
56
+ and ((total_size // downloaded_size) % 5) == 0
57
+ ):
58
+ time.sleep(0.3)
59
+ try:
60
+ client.edit_message_text(
61
+ chat_id,
62
+ message_id,
63
+ text=f"{ud_type}: {humanbytes(downloaded_size)} of {humanbytes(total_size)}",
64
+ )
65
+
66
+ except Exception as e:
67
+ logger.info(f"Error: {e}")
68
+ return
69
+
70
+ return file_name
Uploader/functions/help_ytdl.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
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
+
23
+ import os
24
+ import time
25
+ import requests
26
+ import logging
27
+
28
+ from urllib.parse import urlparse
29
+
30
+ logging.basicConfig(level=logging.DEBUG,
31
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ def get_file_extension_from_url(url):
36
+ url_path = urlparse(url).path
37
+ basename = os.path.basename(url_path)
38
+ return basename.split(".")[-1]
39
+
40
+
41
+ def get_resolution(info_dict):
42
+ if {"width", "height"} <= info_dict.keys():
43
+ width = int(info_dict['width'])
44
+ height = int(info_dict['height'])
45
+ # https://support.google.com/youtube/answer/6375112
46
+ elif info_dict['height'] == 1080:
47
+ width = 1920
48
+ height = 1080
49
+ elif info_dict['height'] == 720:
50
+ width = 1280
51
+ height = 720
52
+ elif info_dict['height'] == 480:
53
+ width = 854
54
+ height = 480
55
+ elif info_dict['height'] == 360:
56
+ width = 640
57
+ height = 360
58
+ elif info_dict['height'] == 240:
59
+ width = 426
60
+ height = 240
61
+ return (width, height)
Uploader/functions/ran_text.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MIT License
2
+
3
+ # Copyright (c) 2022 Hash Minner
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
+
23
+ import random
24
+ import string
25
+
26
+
27
+ def random_char(y):
28
+ return ''.join(random.choice(string.ascii_letters) for _ in range(y))
29
+
30
+
31
+ ran = (random_char(5))