multirandom 0.0.4__tar.gz → 0.0.5__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.
Files changed (23) hide show
  1. {multirandom-0.0.4/src/multirandom.egg-info → multirandom-0.0.5}/PKG-INFO +2 -1
  2. {multirandom-0.0.4 → multirandom-0.0.5}/pyproject.toml +3 -2
  3. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/hash_rand/sha_rand.py +6 -3
  4. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/true_rand/rand_using_online_api.py +1 -1
  5. multirandom-0.0.5/src/multirandom/true_rand/weather_rand.py +281 -0
  6. {multirandom-0.0.4 → multirandom-0.0.5/src/multirandom.egg-info}/PKG-INFO +2 -1
  7. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom.egg-info/requires.txt +1 -0
  8. multirandom-0.0.4/src/multirandom/true_rand/weather_rand.py +0 -2
  9. {multirandom-0.0.4 → multirandom-0.0.5}/LICENSE +0 -0
  10. {multirandom-0.0.4 → multirandom-0.0.5}/README.md +0 -0
  11. {multirandom-0.0.4 → multirandom-0.0.5}/setup.cfg +0 -0
  12. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/__init__.py +0 -0
  13. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/hash_rand/__init__.py +0 -0
  14. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/hash_rand/original_random.py +0 -0
  15. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/true_rand/__init__.py +0 -0
  16. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/true_rand/rand_via_clicks.py +0 -0
  17. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/virt_rand/__init__.py +0 -0
  18. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/virt_rand/flash_rand_and_reverse.py +0 -0
  19. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/virt_rand/rand_using_virt_lcg.py +0 -0
  20. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom/virt_rand/xor_shift.py +0 -0
  21. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom.egg-info/SOURCES.txt +0 -0
  22. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom.egg-info/dependency_links.txt +0 -0
  23. {multirandom-0.0.4 → multirandom-0.0.5}/src/multirandom.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multirandom
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: 🎲 The truth external PyPI random package, have true_rand, virt_rand, hash_rand, ...
5
5
  Author-email: BlackCatOfficialytb <quanvm0501@gmail.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -12,6 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: aiohttp
14
14
  Requires-Dist: pyautogui
15
+ Requires-Dist: geopy
15
16
  Dynamic: license-file
16
17
 
17
18
  # MultiRandom 🎲
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "multirandom"
7
- version = "0.0.4"
7
+ version = "0.0.5"
8
8
  authors = [
9
9
  { name="BlackCatOfficialytb", email="quanvm0501@gmail.com" },
10
10
  ]
@@ -18,7 +18,8 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "aiohttp",
21
- "pyautogui"
21
+ "pyautogui",
22
+ "geopy"
22
23
  ]
23
24
 
24
25
  [project.urls]
@@ -1,4 +1,5 @@
1
- _sha1 = None
1
+ import random
2
+ sha1 = None
2
3
  _sha224 = None
3
4
  _sha256 = None
4
5
  _sha384 = None
@@ -50,9 +51,11 @@ def _get_sha512():
50
51
  # fallback to official implementation
51
52
  from hashlib import sha512 as _sha512
52
53
  return _sha512
53
- class _SHARandom:
54
+ class _SHARandom(random.Random):
54
55
  def __init__(self, seed=None):
55
- self.seed = seed
56
+ if seed is None:
57
+ seed = random.getrandbits(64)
58
+ super().__init__(seed)
56
59
  self.counter = 0
57
60
 
58
61
  def _get_hash(self):
@@ -3,7 +3,7 @@ import asyncio
3
3
 
4
4
  async def fetch_random_org_integers(num: int = 1, min_val: int = 1, max_val: int = 100):
5
5
  """Fetches true random integers from random.org using their plain text API."""
6
- url = "https://www.random.org/integers/"
6
+ url = "https://www.random.org/integers/?"
7
7
  params = {
8
8
  "num": num,
9
9
  "min": min_val,
@@ -0,0 +1,281 @@
1
+ import aiohttp
2
+ import asyncio
3
+ import random
4
+ import hashlib
5
+ from datetime import datetime, timedelta
6
+ from typing import Optional, Tuple
7
+
8
+ """
9
+ A random source using Open-Meteo weather API data.
10
+ While still not fully random and can be spoofed, it's much more reliable than any others cuz it uses hash random from random() too.
11
+ """
12
+
13
+ class OpenMeteoAPI:
14
+ """
15
+ Entropy source using Open-Meteo weather API data.
16
+
17
+ Fetches weather data from random locations and times to generate entropy
18
+ for seeding random.Random. Only provides entropy - use RandomUsingOpenMeteoAPI
19
+ for the full random interface.
20
+ """
21
+
22
+ BASE_URL = "https://api.open-meteo.com/v1/forecast"
23
+
24
+ WEATHER_VARIABLES = [
25
+ "temperature_2m",
26
+ "relative_humidity_2m",
27
+ "precipitation",
28
+ "surface_pressure",
29
+ "wind_speed_10m",
30
+ "wind_direction_10m",
31
+ "cloud_cover"
32
+ ]
33
+
34
+ def __init__(self, seed: Optional[int] = None):
35
+ self._rng = random.Random(seed)
36
+ self._current_location: Optional[Tuple[float, float]] = None
37
+
38
+ def set_current_location(self, latitude: float, longitude: float):
39
+ """Set the current location for weather-based entropy."""
40
+ self._current_location = (latitude, longitude)
41
+
42
+ def _generate_random_location(self) -> Tuple[float, float]:
43
+ """Generate a random latitude and longitude."""
44
+ latitude = self._rng.uniform(-60, 70)
45
+ longitude = self._rng.uniform(-180, 180)
46
+ return (latitude, longitude)
47
+
48
+ def _generate_random_time(self, days_back: int = 7) -> datetime:
49
+ """Generate a random time within the past few days."""
50
+ now = datetime.utcnow()
51
+ random_hours = self._rng.randint(0, days_back * 24)
52
+ return now - timedelta(hours=random_hours)
53
+
54
+ async def _fetch_weather_data(
55
+ self,
56
+ latitude: float,
57
+ longitude: float,
58
+ target_time: Optional[datetime] = None
59
+ ) -> Optional[dict]:
60
+ """Fetch weather data from Open-Meteo API."""
61
+ params = {
62
+ "latitude": latitude,
63
+ "longitude": longitude,
64
+ "hourly": ",".join(self.WEATHER_VARIABLES),
65
+ "timezone": "UTC"
66
+ }
67
+
68
+ if target_time:
69
+ date_str = target_time.strftime("%Y-%m-%d")
70
+ params["start_date"] = date_str
71
+ params["end_date"] = date_str
72
+
73
+ async with aiohttp.ClientSession() as session:
74
+ try:
75
+ async with session.get(self.BASE_URL, params=params) as response:
76
+ if response.status == 200:
77
+ return await response.json()
78
+ return None
79
+ except Exception:
80
+ return None
81
+
82
+ async def _fetch_current_location(self) -> Optional[Tuple[float, float]]:
83
+ """Fetch approximate current location using IP geolocation via geopy."""
84
+ try:
85
+ from geopy.geocoders import Nominatim
86
+
87
+ # First get public IP location using ipinfo.io (free, no key needed)
88
+ async with aiohttp.ClientSession() as session:
89
+ async with session.get("https://ipinfo.io/json") as response:
90
+ if response.status == 200:
91
+ data = await response.json()
92
+ loc = data.get("loc", "")
93
+ if loc:
94
+ lat, lon = loc.split(",")
95
+ return (float(lat), float(lon))
96
+
97
+ # Fallback: use geopy to geocode a random city for entropy
98
+ geolocator = Nominatim(user_agent="multirandom_weather")
99
+ # Pick a random major city for location entropy
100
+ cities = ["Tokyo", "New York", "London", "Sydney", "Mumbai", "São Paulo"]
101
+ city = self._rng.choice(cities)
102
+ location = geolocator.geocode(city)
103
+ if location:
104
+ return (location.latitude, location.longitude)
105
+
106
+ return None
107
+ except Exception:
108
+ return None
109
+
110
+ def _extract_entropy(self, weather_data: dict, target_time: Optional[datetime] = None) -> bytes:
111
+ """Extract entropy bytes from weather data."""
112
+ entropy_values = []
113
+ hourly = weather_data.get("hourly", {})
114
+
115
+ hour_index = 0
116
+ if target_time and "time" in hourly:
117
+ times = hourly.get("time", [])
118
+ target_hour = target_time.strftime("%Y-%m-%dT%H:00")
119
+ if target_hour in times:
120
+ hour_index = times.index(target_hour)
121
+ else:
122
+ hour_index = self._rng.randint(0, max(0, len(times) - 1))
123
+ elif "time" in hourly:
124
+ times = hourly.get("time", [])
125
+ hour_index = self._rng.randint(0, max(0, len(times) - 1))
126
+
127
+ for var in self.WEATHER_VARIABLES:
128
+ values = hourly.get(var, [])
129
+ if values and hour_index < len(values) and values[hour_index] is not None:
130
+ entropy_values.append(values[hour_index])
131
+
132
+ entropy_values.extend([
133
+ weather_data.get("latitude", 0),
134
+ weather_data.get("longitude", 0),
135
+ weather_data.get("elevation", 0),
136
+ datetime.utcnow().timestamp()
137
+ ])
138
+
139
+ entropy_str = "|".join(str(v) for v in entropy_values)
140
+ return hashlib.sha256(entropy_str.encode()).digest()
141
+
142
+ async def get_seed(
143
+ self,
144
+ use_random_location: bool = True,
145
+ use_current_location: bool = False,
146
+ use_random_time: bool = True,
147
+ num_random_locations: int = 2
148
+ ) -> int:
149
+ """
150
+ Get a seed value from weather entropy.
151
+
152
+ Args:
153
+ use_random_location: Fetch weather from random locations
154
+ use_current_location: Fetch weather from current location
155
+ use_random_time: Use random times for weather data
156
+ num_random_locations: Number of random locations to sample
157
+
158
+ Returns:
159
+ Integer seed derived from weather data
160
+ """
161
+ entropy_pool = b""
162
+ tasks = []
163
+
164
+ # Random locations
165
+ if use_random_location:
166
+ for _ in range(num_random_locations):
167
+ lat, lon = self._generate_random_location()
168
+ target_time = self._generate_random_time() if use_random_time else None
169
+ tasks.append((self._fetch_weather_data(lat, lon, target_time), target_time))
170
+
171
+ # Current location
172
+ if use_current_location:
173
+ if self._current_location:
174
+ lat, lon = self._current_location
175
+ else:
176
+ location = await self._fetch_current_location()
177
+ if location:
178
+ lat, lon = location
179
+ self._current_location = location
180
+ else:
181
+ lat, lon = 0, 0
182
+
183
+ target_time = self._generate_random_time() if use_random_time else None
184
+ tasks.append((self._fetch_weather_data(lat, lon, target_time), target_time))
185
+
186
+ # Gather all weather data
187
+ for task, target_time in tasks:
188
+ weather_data = await task
189
+ if weather_data:
190
+ entropy = self._extract_entropy(weather_data, target_time)
191
+ entropy_pool = hashlib.sha256(entropy_pool + entropy).digest()
192
+
193
+ # Fallback if no data gathered
194
+ if not entropy_pool:
195
+ entropy_pool = hashlib.sha256(str(datetime.utcnow().timestamp()).encode()).digest()
196
+
197
+ return int.from_bytes(entropy_pool, 'big')
198
+
199
+
200
+ class RandomUsingOpenMeteoAPI(random.Random):
201
+ """
202
+ A random.Random subclass seeded with weather entropy from Open-Meteo API.
203
+
204
+ Uses weather data to generate a seed, then delegates all random generation
205
+ to the standard Mersenne Twister (random.Random).
206
+ """
207
+
208
+ def __init__(self, auto_seed: bool = True):
209
+ """
210
+ Initialize the random generator.
211
+
212
+ Args:
213
+ auto_seed: If True, automatically seeds with weather entropy.
214
+ """
215
+ super().__init__()
216
+ self._api = OpenMeteoAPI()
217
+
218
+ if auto_seed:
219
+ self.seed_from_weather()
220
+
221
+ def _run_async(self, coro):
222
+ """Helper to run async code synchronously."""
223
+ try:
224
+ asyncio.get_running_loop()
225
+ import concurrent.futures
226
+ with concurrent.futures.ThreadPoolExecutor() as pool:
227
+ future = pool.submit(asyncio.run, coro)
228
+ return future.result()
229
+ except RuntimeError:
230
+ return asyncio.run(coro)
231
+
232
+ def seed_from_weather(
233
+ self,
234
+ use_random_location: bool = True,
235
+ use_current_location: bool = True,
236
+ use_random_time: bool = True
237
+ ):
238
+ """Seed the random generator using weather entropy mixed with hash random."""
239
+ try:
240
+ # Get weather-based seed
241
+ weather_seed = self._run_async(self._api.get_seed(
242
+ use_random_location=use_random_location,
243
+ use_current_location=use_current_location,
244
+ use_random_time=use_random_time
245
+ ))
246
+
247
+ # Mix with original random module's entropy via SHA256
248
+ from ..hash_rand.sha_rand import SHA256Random
249
+ import time
250
+ import os
251
+
252
+ # Create SHA256 random seeded with os.urandom + time
253
+ sha_seed = int.from_bytes(os.urandom(32), 'big') ^ int(time.time() * 1000000)
254
+ sha_rng = SHA256Random(seed=sha_seed)
255
+ sha_entropy = int.from_bytes(sha_rng.getrandbytes(32), 'big')
256
+
257
+ # Also get some entropy from Python's random module
258
+ orig_entropy = int.from_bytes(os.urandom(16), 'big')
259
+
260
+ # Combine all entropy sources with XOR and hash
261
+ combined = weather_seed ^ sha_entropy ^ orig_entropy
262
+ final_seed = int.from_bytes(
263
+ hashlib.sha256(combined.to_bytes(64, 'big')).digest(),
264
+ 'big'
265
+ )
266
+
267
+ super().seed(final_seed)
268
+ except Exception:
269
+ import time
270
+ super().seed(int(time.time() * 1000000))
271
+
272
+ def seed(self, a=None, version=2):
273
+ """Seed the generator. If a is None, seeds from weather."""
274
+ if a is None:
275
+ self.seed_from_weather()
276
+ else:
277
+ super().seed(a, version)
278
+
279
+ def reseed_from_weather(self):
280
+ """Force a reseed from fresh weather data."""
281
+ self.seed_from_weather()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multirandom
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: 🎲 The truth external PyPI random package, have true_rand, virt_rand, hash_rand, ...
5
5
  Author-email: BlackCatOfficialytb <quanvm0501@gmail.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -12,6 +12,7 @@ Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  Requires-Dist: aiohttp
14
14
  Requires-Dist: pyautogui
15
+ Requires-Dist: geopy
15
16
  Dynamic: license-file
16
17
 
17
18
  # MultiRandom 🎲
@@ -1,2 +1,3 @@
1
1
  aiohttp
2
2
  pyautogui
3
+ geopy
@@ -1,2 +0,0 @@
1
- # rand via openweather api and openmeteo api
2
- print("coming soon")
File without changes
File without changes
File without changes