minipy-tools 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: minipy-tools
3
+ Version: 0.1.1
4
+ Author-email: NoΓ© Ballieu <nonobal2812@gmail.com>
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: inquirer
8
+ Requires-Dist: click
9
+ Requires-Dist: python-dotenv
10
+ Requires-Dist: requests
11
+ Requires-Dist: PyPDF2
12
+ Requires-Dist: Pillow
13
+ Requires-Dist: qrcode
14
+
15
+ ![Static Badge](https://img.shields.io/badge/github-repo-blue?logo=github)
16
+
17
+ # **Minipy**
18
+
19
+ Minipy is a CLI tool that combines small/big python tools and games in one big tool.
20
+
21
+ ---
22
+ ## Table of contents
23
+
24
+ - [**Minipy**](#minipy)
25
+ - [Table of contents](#table-of-contents)
26
+ - [Installation](#installation)
27
+ - [File structure](#file-structure)
28
+ - [Configuration](#configuration)
29
+ - [Support](#support)
30
+ - [License](#license)
31
+ - [Status](#status)
32
+
33
+ ---
34
+
35
+ ## Installation
36
+
37
+ To install minipy run this command in your terminal:
38
+
39
+ ```bash
40
+ pip install minipykit
41
+
42
+ ```
43
+
44
+ ---
45
+
46
+ ## File structure
47
+
48
+ This is the file structure of the package:
49
+
50
+ ```md
51
+ └── πŸ“minipy
52
+ └── πŸ“games
53
+ β”œβ”€β”€ snake.py
54
+ └── πŸ“tools
55
+ β”œβ”€β”€ dice.py
56
+ β”œβ”€β”€ link_shortner.py
57
+ β”œβ”€β”€ send_email.py
58
+ β”œβ”€β”€ __init__.py
59
+ └── main.py
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Configuration
65
+
66
+ For some tools/games you need some configuration. The configuration will redirect you to README for instructions. You can find those here:
67
+
68
+ >### Link shortner
69
+
70
+ You need to have a cuttly API key to be able to shorten links.
71
+ To create one follow these steps:
72
+
73
+ 1. Go to [https://cutt.ly](https://cutt.ly)
74
+ 2. Create an account / login
75
+ 3. In your dashboard, go to **API** and then **API key** on the left menu
76
+
77
+ ![menu](image-1.png)
78
+
79
+ 4. You can find your API key on the right side of that page
80
+ 5. Copy it and Paste it in the configuration when asked
81
+
82
+ >### Send email
83
+
84
+ You need to have a Google App-password to be able to send an email.
85
+ This is a second password to your Google account, we can use it in our app to send an mail with Gmail.
86
+ To create one follow these steps:
87
+
88
+ 1. Go to your [Google account settings](https://myaccount.google.com/)
89
+ 2. In the menu on the left click **Security**
90
+ 3. Click on **2-step verification** and ensure it is turned **ON**, if not turn it on
91
+ 4. Scroll down untill you see **App passwords** and click on it
92
+ 5. Type a name for your new App password (this can be anything, like *Email sender*)
93
+ 6. Click on make
94
+ 7. Copy or write down the 16 chars password you see (This is important, because you can't see it again later!)
95
+ 8. Enter it in the configuration when asked
96
+
97
+ ---
98
+
99
+ ## Support
100
+
101
+ For support you can make an **Issue** on the Github repo, thanks for helping with my project.
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ [MIT](https://choosealicense.com/licenses/mit/)
108
+
109
+ ---
110
+
111
+ ## Status
112
+
113
+ Ongoing: I'm still working on this project and constantly adding new tools and features...
114
+
115
+ ---
@@ -0,0 +1,101 @@
1
+ ![Static Badge](https://img.shields.io/badge/github-repo-blue?logo=github)
2
+
3
+ # **Minipy**
4
+
5
+ Minipy is a CLI tool that combines small/big python tools and games in one big tool.
6
+
7
+ ---
8
+ ## Table of contents
9
+
10
+ - [**Minipy**](#minipy)
11
+ - [Table of contents](#table-of-contents)
12
+ - [Installation](#installation)
13
+ - [File structure](#file-structure)
14
+ - [Configuration](#configuration)
15
+ - [Support](#support)
16
+ - [License](#license)
17
+ - [Status](#status)
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ To install minipy run this command in your terminal:
24
+
25
+ ```bash
26
+ pip install minipykit
27
+
28
+ ```
29
+
30
+ ---
31
+
32
+ ## File structure
33
+
34
+ This is the file structure of the package:
35
+
36
+ ```md
37
+ └── πŸ“minipy
38
+ └── πŸ“games
39
+ β”œβ”€β”€ snake.py
40
+ └── πŸ“tools
41
+ β”œβ”€β”€ dice.py
42
+ β”œβ”€β”€ link_shortner.py
43
+ β”œβ”€β”€ send_email.py
44
+ β”œβ”€β”€ __init__.py
45
+ └── main.py
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Configuration
51
+
52
+ For some tools/games you need some configuration. The configuration will redirect you to README for instructions. You can find those here:
53
+
54
+ >### Link shortner
55
+
56
+ You need to have a cuttly API key to be able to shorten links.
57
+ To create one follow these steps:
58
+
59
+ 1. Go to [https://cutt.ly](https://cutt.ly)
60
+ 2. Create an account / login
61
+ 3. In your dashboard, go to **API** and then **API key** on the left menu
62
+
63
+ ![menu](image-1.png)
64
+
65
+ 4. You can find your API key on the right side of that page
66
+ 5. Copy it and Paste it in the configuration when asked
67
+
68
+ >### Send email
69
+
70
+ You need to have a Google App-password to be able to send an email.
71
+ This is a second password to your Google account, we can use it in our app to send an mail with Gmail.
72
+ To create one follow these steps:
73
+
74
+ 1. Go to your [Google account settings](https://myaccount.google.com/)
75
+ 2. In the menu on the left click **Security**
76
+ 3. Click on **2-step verification** and ensure it is turned **ON**, if not turn it on
77
+ 4. Scroll down untill you see **App passwords** and click on it
78
+ 5. Type a name for your new App password (this can be anything, like *Email sender*)
79
+ 6. Click on make
80
+ 7. Copy or write down the 16 chars password you see (This is important, because you can't see it again later!)
81
+ 8. Enter it in the configuration when asked
82
+
83
+ ---
84
+
85
+ ## Support
86
+
87
+ For support you can make an **Issue** on the Github repo, thanks for helping with my project.
88
+
89
+ ---
90
+
91
+ ## License
92
+
93
+ [MIT](https://choosealicense.com/licenses/mit/)
94
+
95
+ ---
96
+
97
+ ## Status
98
+
99
+ Ongoing: I'm still working on this project and constantly adding new tools and features...
100
+
101
+ ---
@@ -0,0 +1 @@
1
+ from .main import main
@@ -0,0 +1,171 @@
1
+ from tkinter import *
2
+ import random
3
+
4
+ GAME_WIDTH = 1000
5
+ GAME_HEIGHT = 700
6
+ SPEED = 100
7
+ SPACE_SIZE = 40
8
+ BODY_PARTS = 3
9
+ SNAKE_COLOR = '#00FF00'
10
+ FOOD_COLOR = 'red'
11
+ BACKGROUND_COLOR = 'black'
12
+
13
+ class Snake:
14
+ def __init__(self):
15
+ self.body_size = BODY_PARTS
16
+ self.coordinates = []
17
+ self.squares = []
18
+
19
+ for i in range(0, BODY_PARTS):
20
+ self.coordinates.append([0,0])
21
+
22
+ for x, y in self.coordinates:
23
+ square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR, tags="snake")
24
+ self.squares.append(square)
25
+
26
+ class Food:
27
+ def __init__(self):
28
+
29
+ x = random.randint(0, (GAME_WIDTH // SPACE_SIZE)-1) * SPACE_SIZE
30
+ y = random.randint(0, (GAME_HEIGHT // SPACE_SIZE)-1) * SPACE_SIZE
31
+
32
+ self.coordinates = [x,y]
33
+
34
+ canvas.create_oval(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=FOOD_COLOR, tag="food")
35
+
36
+ def next_turn(snake, food):
37
+
38
+ x, y = snake.coordinates[0]
39
+
40
+ if direction == "up":
41
+ y -= SPACE_SIZE
42
+ elif direction == "down":
43
+ y += SPACE_SIZE
44
+ elif direction == "left":
45
+ x -= SPACE_SIZE
46
+ elif direction == "right":
47
+ x += SPACE_SIZE
48
+
49
+ snake.coordinates.insert(0, (x,y))
50
+
51
+ square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR)
52
+
53
+ snake.squares.insert(0, square)
54
+
55
+ if x == food.coordinates[0] and y == food.coordinates[1]:
56
+
57
+ global score
58
+
59
+ score += 1
60
+
61
+ label.config(text="Score:{}".format(score))
62
+
63
+ canvas.delete("food")
64
+
65
+ food = Food()
66
+ else:
67
+
68
+ del snake.coordinates[-1]
69
+
70
+ canvas.delete(snake.squares[-1])
71
+
72
+ del snake.squares[-1]
73
+
74
+ if check_collisions(snake):
75
+ game_over()
76
+ else:
77
+ window.after(SPEED, next_turn, snake, food)
78
+
79
+
80
+ def change_direction(new_direction):
81
+
82
+ global direction
83
+
84
+ if new_direction == 'left':
85
+ if direction != 'right':
86
+ direction = new_direction
87
+
88
+ elif new_direction == 'right':
89
+ if direction != 'left':
90
+ direction = new_direction
91
+
92
+ elif new_direction == 'up':
93
+ if direction != 'down':
94
+ direction = new_direction
95
+
96
+ elif new_direction == 'down':
97
+ if direction != 'up':
98
+ direction = new_direction
99
+
100
+ def check_collisions(snake):
101
+
102
+ x, y = snake.coordinates[0]
103
+
104
+ if x < 0 or x >= GAME_WIDTH:
105
+ return True
106
+ elif y < 0 or y >= GAME_HEIGHT:
107
+ return True
108
+
109
+ for body_part in snake.coordinates[1:]:
110
+ if x == body_part[0] and y == body_part[1]:
111
+ return True
112
+
113
+ return False
114
+
115
+ def game_over():
116
+ canvas.delete(ALL)
117
+ canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
118
+ font=('consolas', 70), text="GAME OVER", fill="red", tag="gameover")
119
+ restart_btn = Button(window, text="Restart", font=('consolas', 20), command=restart_game)
120
+ canvas.create_window(canvas.winfo_width()/2, canvas.winfo_height()/2 + 100, window=restart_btn)
121
+
122
+ def restart_game():
123
+ global snake, food, score, direction
124
+
125
+ canvas.delete(ALL)
126
+
127
+ score = 0
128
+ direction = 'down'
129
+ label.config(text="Score:{}".format(score))
130
+
131
+ snake = Snake()
132
+ food = Food()
133
+
134
+ next_turn(snake, food)
135
+
136
+ window = Tk()
137
+ window.title('Snake game 🐍')
138
+ window.resizable(False, False)
139
+
140
+ score = 0
141
+ direction = 'down'
142
+
143
+ label = Label(window, text="Score:{}".format(score), font=('consolas', 40))
144
+ label.pack()
145
+
146
+ canvas = Canvas(window, bg=BACKGROUND_COLOR, height=GAME_HEIGHT, width=GAME_WIDTH, highlightthickness=3, highlightbackground='yellow')
147
+ canvas.pack()
148
+
149
+ window.update()
150
+
151
+ window_width = window.winfo_width()
152
+ window_height = window.winfo_height()
153
+ screen_width = window.winfo_screenwidth()
154
+ screen_height = window.winfo_screenheight()
155
+
156
+ x = int((screen_width/2) - (window_width/2))
157
+ y = int((screen_height/2) - (window_height/2))
158
+
159
+ window.geometry(f"{window_width}x{window_height}+{x}+{y}")
160
+
161
+ snake = Snake()
162
+ food = Food()
163
+
164
+ window.bind('<Left>', lambda event: change_direction('left'))
165
+ window.bind('<Right>', lambda event: change_direction('right'))
166
+ window.bind('<Up>', lambda event: change_direction('up'))
167
+ window.bind('<Down>', lambda event: change_direction('down'))
168
+
169
+ next_turn(snake, food)
170
+
171
+ window.mainloop()
@@ -0,0 +1,98 @@
1
+ # extensions
2
+ # - Work with Rich and make a cool graphical interface
3
+
4
+ import subprocess
5
+ from pathlib import Path
6
+ import inquirer
7
+ import sys
8
+ import os
9
+ import time
10
+
11
+ SRC = Path(__file__).parent
12
+
13
+ BANNER = r"""
14
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
15
+ | __ __ ___ _ _ ___ ____ __ __ |
16
+ | | \/ | |_ _| | \ | | |_ _| | _ \ \ \ / / |
17
+ | | |\/| |/ | | \| \| |/ | | \| |_) | /\ V / |
18
+ | |_| |_| |___| |_| \_| |___| | .__/ | | |
19
+ | |_| |_| |
20
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
21
+
22
+ """
23
+ def main():
24
+ while True:
25
+ clear_screen()
26
+ print(BANNER)
27
+
28
+ category = ask("Choose a category: ",
29
+ categorys() + ["\033[1;31mStop\033[0m"],)
30
+ if category == "\033[1;31mStop\033[0m":
31
+ clear_screen()
32
+ print("Bye!")
33
+ break
34
+
35
+ clear_screen()
36
+ print(BANNER)
37
+
38
+ project = ask(
39
+ f"Choose a project in {category}",
40
+ projects(category) + ["\033[1;33mBack\033[0m"],
41
+ )
42
+
43
+ if project != "\033[1;33mBack\033[0m":
44
+ start(category, project)
45
+
46
+
47
+ if project == "\033[1;33mBack\033[0m":
48
+ continue
49
+
50
+ def categorys():
51
+ return sorted(file.name for file in SRC.iterdir() if file.is_dir() and file.name != "venv" and file.name != "__pycache__" and not file.name.startswith("."))
52
+
53
+ def projects(category):
54
+ return sorted(
55
+ file.stem
56
+ for file in (SRC / category).iterdir()
57
+ if file.is_file() and file.suffix == ".py"
58
+ )
59
+
60
+ def ask(text, choices):
61
+ answer = inquirer.prompt([
62
+ inquirer.List("choice", message=text, choices=choices)
63
+ ])
64
+ return answer["choice"]
65
+
66
+ def start(category, name):
67
+ pad = SRC / category / f"{name}.py"
68
+ clear_screen()
69
+
70
+ clean_name = name.replace("_", " ").upper()
71
+
72
+ print("\033[1;36m---------------------------------------------------")
73
+ print(f"--> OPEN APP: {clean_name} , Close app with Ctrl+C")
74
+ print("---------------------------------------------------\033[0m")
75
+ print()
76
+
77
+ try:
78
+ subprocess.run([sys.executable, str(pad)], check=True)
79
+ print()
80
+ print("---------------------------------------------------")
81
+ input("Press Enter to return to the menu...")
82
+ except (KeyboardInterrupt, subprocess.CalledProcessError):
83
+ # clear_screen()
84
+ print("\n\nApp closed with Ctrl+C. Returning to main menu...")
85
+ time.sleep(10)
86
+
87
+
88
+
89
+
90
+
91
+ def clear_screen():
92
+ if os.name =='nt':
93
+ subprocess.run(["cmd", "/c", "cls"])
94
+ else:
95
+ subprocess.run(["clear"])
96
+
97
+ if __name__ == "__main__":
98
+ main()
@@ -0,0 +1,44 @@
1
+ import qrcode
2
+ from pathlib import Path
3
+ import tkinter as tk
4
+ from PIL import ImageTk
5
+
6
+ data = input("Enter the text or URL you want the QR-code to redirect to: ").strip()
7
+ while not data:
8
+ data = input("Input cannot be empty, please enter text or URL: ").strip()
9
+
10
+
11
+
12
+ def generate_qr():
13
+ img = qrcode.make(data)
14
+
15
+ root = tk.Tk()
16
+ root.title("Your QR")
17
+
18
+ tk_img = ImageTk.PhotoImage(img)
19
+
20
+ label = tk.Label(root, image=tk_img)
21
+ label.pack(padx=10, pady=10)
22
+
23
+ root.update()
24
+
25
+ choice = input("Do you want to save the QR-code to your Downloads folder? (Y/N): ").strip().lower()
26
+
27
+ if choice =="y":
28
+ fileName = input("What should the file be named? (without extension): ").strip()
29
+
30
+ while not fileName:
31
+ fileName = input("Input cannot be empty, please enter a file name: ").strip()
32
+
33
+ downloads_folder = Path.home() / "Downloads"
34
+ img.save(downloads_folder / f"{fileName}.png")
35
+ print(f"QR-code saved in Downloads as: {fileName}.png")
36
+ else:
37
+ print("QR-code not saved")
38
+
39
+ print("Thank you for using!")
40
+ root.destroy()
41
+
42
+
43
+
44
+ generate_qr()
@@ -0,0 +1,13 @@
1
+ from random import randint
2
+
3
+ while True:
4
+ responce = input("Roll the dice? (Y/N): ").lower()
5
+ if responce == "y":
6
+ print(randint(1,6))
7
+ elif responce =="n":
8
+ print("Bye! thanks for using.")
9
+ break
10
+ else:
11
+ print("Please choose an option (Y/N): ")
12
+
13
+
@@ -0,0 +1,80 @@
1
+ import requests
2
+ import os
3
+ from PIL import Image
4
+ from io import BytesIO
5
+ from tkinter import Tk, Label
6
+ from PIL import ImageTk
7
+ from dotenv import load_dotenv, set_key
8
+ from pathlib import Path
9
+
10
+ CONFIG_DIR = Path.home() / ".minipy"
11
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
12
+ ENV_PATH = CONFIG_DIR / ".env"
13
+ BASE_URL = 'https://ip-intelligence.abstractapi.com/v1/'
14
+
15
+
16
+ def show_img(flag_image_url):
17
+ response = requests.get(flag_image_url)
18
+ img = Image.open(BytesIO(response.content))
19
+
20
+ root = Tk()
21
+ root.title("Flag")
22
+
23
+ photo = ImageTk.PhotoImage(img)
24
+ label = Label(root, image=photo)
25
+ label.pack()
26
+
27
+ root.mainloop()
28
+
29
+
30
+
31
+ def config():
32
+ if not ENV_PATH.exists():
33
+ ENV_PATH.touch()
34
+
35
+ load_dotenv(dotenv_path=ENV_PATH)
36
+
37
+ api_key = os.getenv("Abstract_IP_API_key")
38
+
39
+ if not api_key:
40
+ print("This is the first time you're using this tool.")
41
+ print("We're gonna do some configuration.\n")
42
+ print("Read the instruction in the \033]8;;https://github.com/noamopilo/minipy/blob/main/README.md\033\\README\033]8;;\033\\ on how to create a Abstract ip API key")
43
+ api_key = input("Input you Abstract ip API key: ").strip()
44
+ set_key(str(ENV_PATH), "Abstract_IP_API_key", api_key)
45
+
46
+ print("Configuration succesfull!\n")
47
+ load_dotenv(dotenv_path=ENV_PATH)
48
+
49
+ return os.getenv("Abstract_IP_API_key")
50
+
51
+ def locate_ip():
52
+ API_KEY = config()
53
+ ip_address = input("What is the public IP address you want to loacte?: ")
54
+ params = {
55
+ "api_key": API_KEY,
56
+ "ip_address": ip_address
57
+ }
58
+
59
+ request = requests.get(BASE_URL, params=params)
60
+ data = request.json()
61
+
62
+ try:
63
+ country = data['location']['country']
64
+ country_code = data['location']['country_code']
65
+ city = data['location']['city']
66
+ continent = data['location']['continent']
67
+ local_time = data['timezone']['local_time']
68
+ flag_image_url = data['flag']['png']
69
+
70
+ print(f"This IP address ({ip_address}) is located in {city}, {country}({country_code}), {continent}")
71
+ print(f"Local time at location: {local_time}")
72
+ answer = input("Do you want to see the flag of this country? (Y/N): ").lower().strip()
73
+
74
+ if answer == "y":
75
+ print("Image opening in new window...")
76
+ show_img(flag_image_url)
77
+ except:
78
+ print("Something went wrong, please check your API key, IP adress or Internet connection and try again.")
79
+
80
+ locate_ip()
@@ -0,0 +1,58 @@
1
+ import requests
2
+ import os
3
+ from dotenv import load_dotenv, set_key
4
+ from pathlib import Path
5
+
6
+ CONFIG_DIR = Path.home() / ".minipy"
7
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
8
+ ENV_PATH = CONFIG_DIR / ".env"
9
+ BASE_URL = 'https://cutt.ly/api/api.php'
10
+
11
+
12
+ def config():
13
+ if not ENV_PATH.exists():
14
+ ENV_PATH.touch()
15
+
16
+ load_dotenv(dotenv_path=ENV_PATH)
17
+
18
+ api_key = os.getenv("CUTTLY_API_KEY")
19
+
20
+ if not api_key:
21
+ print("This is the first time you're using this tool.")
22
+ print("We're gonna do some configuration.\n")
23
+ print("Read the instructions in the \033]8;;https://github.com/noamopilo/minipy/blob/main/README.md\033\\README\033]8;;\033\\ on how to create a cuttly API key.")
24
+
25
+ api_key = input("Input your cuttly API key: ").strip()
26
+ set_key(str(ENV_PATH), "CUTTLY_API_KEY", api_key)
27
+
28
+ print("Configuration succesfull!\n")
29
+ load_dotenv(dotenv_path=ENV_PATH)
30
+
31
+ return os.getenv("CUTTLY_API_KEY")
32
+
33
+ def shorten_url(long_url, alias, API_KEY):
34
+ params = {
35
+ 'key': API_KEY,
36
+ 'short': long_url,
37
+ 'name': alias
38
+ }
39
+
40
+ request = requests.get(BASE_URL, params=params)
41
+ data = request.json()
42
+
43
+ try:
44
+ title = data['url']['title']
45
+ short_link = data['url']['shortLink']
46
+ print('Title: ', title)
47
+ print('Short url: ', short_link)
48
+ except:
49
+ status = data['url']['status']
50
+ if status == 3:
51
+ status = 'Alias is already taken'
52
+ print('Error status: ', status)
53
+
54
+ if __name__ == "__main__":
55
+ API_KEY = config()
56
+ long_url = input("What is the long url you wnat to shorten?: ")
57
+ alias = input("What is the alias you want for yout short url? (https://cutt.ly/alias): ")
58
+ shorten_url(long_url, alias, API_KEY)
@@ -0,0 +1,47 @@
1
+ import PyPDF2
2
+ import sys
3
+ import os
4
+ import shutil
5
+ from pathlib import Path
6
+
7
+ downloads_path = Path.home() / "Downloads"
8
+ folder_path = downloads_path / "minipy_PDFS"
9
+
10
+ if not folder_path.exists():
11
+ folder_path.mkdir(parents=True, exist_ok=True)
12
+
13
+
14
+ merger = PyPDF2.PdfMerger()
15
+ pdf = "no"
16
+
17
+ print(f"Put the pdfs that you want merged in the /minipy_PDFS folder in your Downloads (this is a temporary folder and will be deleted after merging)\n")
18
+ input("Press enter when done...")
19
+
20
+ while pdf == "no":
21
+ filename = input("What should be the name of the merged pdf? (with .pdf): ")
22
+
23
+ if not filename.endswith(".pdf"):
24
+ print("The file name must end with .pdf")
25
+ else:
26
+ pdf ="yes"
27
+
28
+ pdf_files = [f for f in os.listdir(folder_path) if f.endswith(".pdf")]
29
+
30
+ if not pdf_files:
31
+ print("No PDF files found, merging cancceled..")
32
+ shutil.rmtree(folder_path)
33
+ sys.exit()
34
+
35
+ try:
36
+ for file in sorted(pdf_files):
37
+ print(f"Merging {file}...")
38
+ full_path = os.path.join(folder_path, file)
39
+ merger.append(full_path)
40
+
41
+ output_path = downloads_path / filename
42
+ merger.write(str(output_path))
43
+ merger.close()
44
+
45
+ shutil.rmtree(folder_path)
46
+ finally:
47
+ shutil.rmtree(folder_path)
@@ -0,0 +1,89 @@
1
+ # extension:
2
+ # - multi receiver email system
3
+ # - beginning menu (send email, configuration, send multiple emails)
4
+
5
+
6
+ import os
7
+ import click
8
+ import smtplib
9
+ from pathlib import Path
10
+ from email.message import EmailMessage
11
+ from dotenv import load_dotenv, set_key
12
+
13
+ CONFIG_DIR = Path.home() / ".minipy"
14
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
15
+ ENV_PATH = CONFIG_DIR / ".env"
16
+
17
+ def config():
18
+ if not ENV_PATH.exists():
19
+ ENV_PATH.touch()
20
+
21
+ load_dotenv(dotenv_path=ENV_PATH)
22
+
23
+ password = os.getenv("GOOGLE_APP_PASS")
24
+ email = os.getenv("SENDER_EMAIL")
25
+
26
+ if not password or not email:
27
+ print("This is the first time you use this tool.")
28
+ print("We're gonna do the configuration.\n")
29
+ print("Read the instructions in the \033]8;;https://github.com/noamopilo/minipy/blob/main/README.md\033\\README\033]8;;\033\\ on how to create a Google App Password.")
30
+
31
+ if not email:
32
+ email = input("Input your Gmail-adress: ").strip()
33
+ set_key(str(ENV_PATH), "SENDER_EMAIL", email)
34
+
35
+ if not password:
36
+ password = input("Input your Google App Password: ").strip()
37
+ set_key(str(ENV_PATH), "GOOGLE_APP_PASS", password)
38
+
39
+ print("Configuration succesfull!\n")
40
+
41
+ load_dotenv(dotenv_path=ENV_PATH)
42
+
43
+ return os.getenv("SENDER_EMAIL"), os.getenv("GOOGLE_APP_PASS")
44
+
45
+ def get_multiline_input_editor():
46
+ instruction_text = "# Type your message above. Save the file and close it to send."
47
+ message = click.edit(f"\n\n{instruction_text}")
48
+
49
+ if message:
50
+ good_message = message.replace(instruction_text, "").strip()
51
+ return good_message
52
+ return ""
53
+
54
+ def send_email(sender, password, receiver, subject, message):
55
+ msg = EmailMessage()
56
+ msg['Subject'] = subject
57
+ msg['From'] = sender
58
+ msg['To'] = receiver
59
+
60
+ msg.set_content(message)
61
+
62
+ print("\n ***Sending email...")
63
+ try:
64
+ with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
65
+ smtp.login(sender, password)
66
+ smtp.send_message(msg)
67
+ print("Email sent! --> ")
68
+ except:
69
+ print(f"Failed to send email. Error: {Exception}")
70
+
71
+
72
+ if __name__ == "__main__":
73
+ user_email, user_password = config()
74
+ print(f"User is: {user_email}")
75
+ receiver = input("Who do you want to send an email to?: ")
76
+ subject = input("What is the subject of the email?: ")
77
+ action = input("Type 'E' to open the editor: ").strip().lower()
78
+ if action == "e":
79
+ print("Editor is opening...")
80
+ message = get_multiline_input_editor()
81
+
82
+ if not message.strip():
83
+ print("Message is empty. Sending canceled!")
84
+ else:
85
+ print("\n--- Your message ---")
86
+ print(message)
87
+ print("--------------------")
88
+ send_email(user_email, user_password, receiver, subject, message)
89
+
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: minipy-tools
3
+ Version: 0.1.1
4
+ Author-email: NoΓ© Ballieu <nonobal2812@gmail.com>
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: inquirer
8
+ Requires-Dist: click
9
+ Requires-Dist: python-dotenv
10
+ Requires-Dist: requests
11
+ Requires-Dist: PyPDF2
12
+ Requires-Dist: Pillow
13
+ Requires-Dist: qrcode
14
+
15
+ ![Static Badge](https://img.shields.io/badge/github-repo-blue?logo=github)
16
+
17
+ # **Minipy**
18
+
19
+ Minipy is a CLI tool that combines small/big python tools and games in one big tool.
20
+
21
+ ---
22
+ ## Table of contents
23
+
24
+ - [**Minipy**](#minipy)
25
+ - [Table of contents](#table-of-contents)
26
+ - [Installation](#installation)
27
+ - [File structure](#file-structure)
28
+ - [Configuration](#configuration)
29
+ - [Support](#support)
30
+ - [License](#license)
31
+ - [Status](#status)
32
+
33
+ ---
34
+
35
+ ## Installation
36
+
37
+ To install minipy run this command in your terminal:
38
+
39
+ ```bash
40
+ pip install minipykit
41
+
42
+ ```
43
+
44
+ ---
45
+
46
+ ## File structure
47
+
48
+ This is the file structure of the package:
49
+
50
+ ```md
51
+ └── πŸ“minipy
52
+ └── πŸ“games
53
+ β”œβ”€β”€ snake.py
54
+ └── πŸ“tools
55
+ β”œβ”€β”€ dice.py
56
+ β”œβ”€β”€ link_shortner.py
57
+ β”œβ”€β”€ send_email.py
58
+ β”œβ”€β”€ __init__.py
59
+ └── main.py
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Configuration
65
+
66
+ For some tools/games you need some configuration. The configuration will redirect you to README for instructions. You can find those here:
67
+
68
+ >### Link shortner
69
+
70
+ You need to have a cuttly API key to be able to shorten links.
71
+ To create one follow these steps:
72
+
73
+ 1. Go to [https://cutt.ly](https://cutt.ly)
74
+ 2. Create an account / login
75
+ 3. In your dashboard, go to **API** and then **API key** on the left menu
76
+
77
+ ![menu](image-1.png)
78
+
79
+ 4. You can find your API key on the right side of that page
80
+ 5. Copy it and Paste it in the configuration when asked
81
+
82
+ >### Send email
83
+
84
+ You need to have a Google App-password to be able to send an email.
85
+ This is a second password to your Google account, we can use it in our app to send an mail with Gmail.
86
+ To create one follow these steps:
87
+
88
+ 1. Go to your [Google account settings](https://myaccount.google.com/)
89
+ 2. In the menu on the left click **Security**
90
+ 3. Click on **2-step verification** and ensure it is turned **ON**, if not turn it on
91
+ 4. Scroll down untill you see **App passwords** and click on it
92
+ 5. Type a name for your new App password (this can be anything, like *Email sender*)
93
+ 6. Click on make
94
+ 7. Copy or write down the 16 chars password you see (This is important, because you can't see it again later!)
95
+ 8. Enter it in the configuration when asked
96
+
97
+ ---
98
+
99
+ ## Support
100
+
101
+ For support you can make an **Issue** on the Github repo, thanks for helping with my project.
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ [MIT](https://choosealicense.com/licenses/mit/)
108
+
109
+ ---
110
+
111
+ ## Status
112
+
113
+ Ongoing: I'm still working on this project and constantly adding new tools and features...
114
+
115
+ ---
@@ -0,0 +1,17 @@
1
+ README.md
2
+ pyproject.toml
3
+ minipy/__init__.py
4
+ minipy/main.py
5
+ minipy/games/snake.py
6
+ minipy/tools/QR_code_generator.py
7
+ minipy/tools/dice.py
8
+ minipy/tools/ip_locator.py
9
+ minipy/tools/link_shortner.py
10
+ minipy/tools/pdf_merger.py
11
+ minipy/tools/send_email.py
12
+ minipy_tools.egg-info/PKG-INFO
13
+ minipy_tools.egg-info/SOURCES.txt
14
+ minipy_tools.egg-info/dependency_links.txt
15
+ minipy_tools.egg-info/entry_points.txt
16
+ minipy_tools.egg-info/requires.txt
17
+ minipy_tools.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ minipy = minipy.main:main
@@ -0,0 +1,7 @@
1
+ inquirer
2
+ click
3
+ python-dotenv
4
+ requests
5
+ PyPDF2
6
+ Pillow
7
+ qrcode
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "minipy-tools"
7
+ version = "0.1.1"
8
+ description = ""
9
+ authors = [{name = "NoΓ© Ballieu", email = "nonobal2812@gmail.com"}]
10
+ readme = "README.md"
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "inquirer",
14
+ "click",
15
+ "python-dotenv",
16
+ "requests",
17
+ "PyPDF2",
18
+ "Pillow",
19
+ "qrcode"
20
+ ]
21
+
22
+ [project.scripts]
23
+ minipy = "minipy.main:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+