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.
- minipy_tools-0.1.1/PKG-INFO +115 -0
- minipy_tools-0.1.1/README.md +101 -0
- minipy_tools-0.1.1/minipy/__init__.py +1 -0
- minipy_tools-0.1.1/minipy/games/snake.py +171 -0
- minipy_tools-0.1.1/minipy/main.py +98 -0
- minipy_tools-0.1.1/minipy/tools/QR_code_generator.py +44 -0
- minipy_tools-0.1.1/minipy/tools/dice.py +13 -0
- minipy_tools-0.1.1/minipy/tools/ip_locator.py +80 -0
- minipy_tools-0.1.1/minipy/tools/link_shortner.py +58 -0
- minipy_tools-0.1.1/minipy/tools/pdf_merger.py +47 -0
- minipy_tools-0.1.1/minipy/tools/send_email.py +89 -0
- minipy_tools-0.1.1/minipy_tools.egg-info/PKG-INFO +115 -0
- minipy_tools-0.1.1/minipy_tools.egg-info/SOURCES.txt +17 -0
- minipy_tools-0.1.1/minipy_tools.egg-info/dependency_links.txt +1 -0
- minipy_tools-0.1.1/minipy_tools.egg-info/entry_points.txt +2 -0
- minipy_tools-0.1.1/minipy_tools.egg-info/requires.txt +7 -0
- minipy_tools-0.1.1/minipy_tools.egg-info/top_level.txt +1 -0
- minipy_tools-0.1.1/pyproject.toml +23 -0
- minipy_tools-0.1.1/setup.cfg +4 -0
|
@@ -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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
minipy
|
|
@@ -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"
|