session-python 1.0.0__tar.gz → 1.0.2__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.
- {session_python-1.0.0/session_python.egg-info → session_python-1.0.2}/PKG-INFO +3 -3
- {session_python-1.0.0 → session_python-1.0.2}/README.md +2 -2
- {session_python-1.0.0 → session_python-1.0.2}/pyproject.toml +4 -1
- session_python-1.0.2/session_python/cli.py +233 -0
- {session_python-1.0.0 → session_python-1.0.2/session_python.egg-info}/PKG-INFO +3 -3
- {session_python-1.0.0 → session_python-1.0.2}/session_python.egg-info/SOURCES.txt +2 -0
- session_python-1.0.2/session_python.egg-info/entry_points.txt +2 -0
- {session_python-1.0.0 → session_python-1.0.2}/LICENSE +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/__init__.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/client.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/crypto.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/mnemonic.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/network.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/polling.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/protobuf/__init__.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python/protobuf/signalservice_pb2.py +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python.egg-info/dependency_links.txt +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python.egg-info/requires.txt +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/session_python.egg-info/top_level.txt +0 -0
- {session_python-1.0.0 → session_python-1.0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: session-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: A pure Python implementation of the Session Messenger protocol client
|
|
5
5
|
Author-email: Towux <towux@users.noreply.github.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/towux/session-python
|
|
@@ -51,10 +51,10 @@ This library is a 1-to-1 port of the official `@session.js` (Bun) client modules
|
|
|
51
51
|
|
|
52
52
|
## 📦 Installation
|
|
53
53
|
|
|
54
|
-
To install `session-python
|
|
54
|
+
To install `session-python`:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
pip install
|
|
57
|
+
pip install session-python
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
---
|
|
@@ -29,10 +29,10 @@ This library is a 1-to-1 port of the official `@session.js` (Bun) client modules
|
|
|
29
29
|
|
|
30
30
|
## 📦 Installation
|
|
31
31
|
|
|
32
|
-
To install `session-python
|
|
32
|
+
To install `session-python`:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
pip install
|
|
35
|
+
pip install session-python
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
---
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "session-python"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "A pure Python implementation of the Session Messenger protocol client"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -26,6 +26,9 @@ dependencies = [
|
|
|
26
26
|
"pysocks>=1.7.1"
|
|
27
27
|
]
|
|
28
28
|
|
|
29
|
+
[project.scripts]
|
|
30
|
+
sscli = "session_python.cli:main"
|
|
31
|
+
|
|
29
32
|
[project.urls]
|
|
30
33
|
"Homepage" = "https://github.com/towux/session-python"
|
|
31
34
|
"Bug Tracker" = "https://github.com/towux/session-python/issues"
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import time
|
|
3
|
+
import os
|
|
4
|
+
import binascii
|
|
5
|
+
import ctypes
|
|
6
|
+
import multiprocessing
|
|
7
|
+
import nacl.signing
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.live import Live
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
from .client import Session
|
|
12
|
+
from .mnemonic import encode as encode_mnemonic, decode as decode_mnemonic
|
|
13
|
+
|
|
14
|
+
def format_time(seconds: float) -> str:
|
|
15
|
+
if seconds <= 0:
|
|
16
|
+
return "0s"
|
|
17
|
+
MINUTE = 60
|
|
18
|
+
HOUR = 3600
|
|
19
|
+
DAY = 86400
|
|
20
|
+
MONTH = 2592000 # 30 days
|
|
21
|
+
YEAR = 31536000 # 365 days
|
|
22
|
+
|
|
23
|
+
if seconds < MINUTE:
|
|
24
|
+
return f"{seconds:.0f}s"
|
|
25
|
+
if seconds < HOUR:
|
|
26
|
+
m = int(seconds // MINUTE)
|
|
27
|
+
s = int(seconds % MINUTE)
|
|
28
|
+
return f"{m}m {s}s"
|
|
29
|
+
if seconds < DAY:
|
|
30
|
+
h = int(seconds // HOUR)
|
|
31
|
+
m = int((seconds % HOUR) // MINUTE)
|
|
32
|
+
return f"{h}h {m}m"
|
|
33
|
+
if seconds < MONTH:
|
|
34
|
+
d = int(seconds // DAY)
|
|
35
|
+
h = int((seconds % DAY) // HOUR)
|
|
36
|
+
return f"{d}d {h}h"
|
|
37
|
+
if seconds < YEAR:
|
|
38
|
+
mo = int(seconds // MONTH)
|
|
39
|
+
d = int((seconds % MONTH) // DAY)
|
|
40
|
+
return f"{mo}mo {d}d"
|
|
41
|
+
|
|
42
|
+
y = int(seconds // YEAR)
|
|
43
|
+
mo = int((seconds % YEAR) // MONTH)
|
|
44
|
+
return f"{y}y {mo}mo"
|
|
45
|
+
|
|
46
|
+
def search_worker(prefix: str, result_queue: multiprocessing.Queue, checked_counter: multiprocessing.Value, stop_event: multiprocessing.Event):
|
|
47
|
+
"""
|
|
48
|
+
Worker process for brute-forcing vanity Session IDs.
|
|
49
|
+
"""
|
|
50
|
+
import os
|
|
51
|
+
import binascii
|
|
52
|
+
import nacl.signing
|
|
53
|
+
from .mnemonic import encode as encode_mnemonic
|
|
54
|
+
|
|
55
|
+
local_checked = 0
|
|
56
|
+
while not stop_event.is_set():
|
|
57
|
+
local_checked += 1
|
|
58
|
+
seed_16 = os.urandom(16)
|
|
59
|
+
|
|
60
|
+
# Derive Session ID
|
|
61
|
+
seed_32_hex = binascii.hexlify(seed_16).decode('utf-8') + "00000000000000000000000000000000"
|
|
62
|
+
seed_32 = binascii.unhexlify(seed_32_hex)
|
|
63
|
+
|
|
64
|
+
signing_key = nacl.signing.SigningKey(seed_32)
|
|
65
|
+
x25519_pub = signing_key.verify_key.to_curve25519_public_key()
|
|
66
|
+
session_id = "05" + x25519_pub.encode().hex()
|
|
67
|
+
|
|
68
|
+
if session_id[2:].startswith(prefix):
|
|
69
|
+
mnemonic = encode_mnemonic(binascii.hexlify(seed_16).decode('utf-8'))
|
|
70
|
+
result_queue.put((session_id, mnemonic))
|
|
71
|
+
stop_event.set()
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
# Update shared counter periodically to avoid locking bottleneck
|
|
75
|
+
if local_checked >= 2000:
|
|
76
|
+
with checked_counter.get_lock():
|
|
77
|
+
checked_counter.value += local_checked
|
|
78
|
+
local_checked = 0
|
|
79
|
+
|
|
80
|
+
if local_checked > 0:
|
|
81
|
+
with checked_counter.get_lock():
|
|
82
|
+
checked_counter.value += local_checked
|
|
83
|
+
|
|
84
|
+
def main():
|
|
85
|
+
multiprocessing.freeze_support()
|
|
86
|
+
|
|
87
|
+
max_threads = os.cpu_count() or 1
|
|
88
|
+
|
|
89
|
+
parser = argparse.ArgumentParser(
|
|
90
|
+
description="Session Python CLI - Command Line interface for Session Messenger"
|
|
91
|
+
)
|
|
92
|
+
subparsers = parser.add_subparsers(dest="command", help="Sub-commands")
|
|
93
|
+
|
|
94
|
+
# 'mnemonic' group command
|
|
95
|
+
mnemonic_parser = subparsers.add_parser("mnemonic", help="Mnemonic and account key utilities")
|
|
96
|
+
mnemonic_subparsers = mnemonic_parser.add_subparsers(dest="subcommand", help="Sub-commands of mnemonic")
|
|
97
|
+
|
|
98
|
+
# 'mnemonic verify'
|
|
99
|
+
verify_parser = mnemonic_subparsers.add_parser("verify", help="Verify a 13-word mnemonic")
|
|
100
|
+
verify_parser.add_argument("words", type=str, help="The 13-word mnemonic to verify")
|
|
101
|
+
|
|
102
|
+
# 'mnemonic gen'
|
|
103
|
+
gen_parser = mnemonic_subparsers.add_parser("gen", help="Generate new mnemonics or derive ID from existing mnemonic")
|
|
104
|
+
gen_parser.add_argument("words", type=str, nargs="?", default=None, help="Optional existing 13-word mnemonic to derive ID from")
|
|
105
|
+
gen_parser.add_argument(
|
|
106
|
+
"-n",
|
|
107
|
+
type=int,
|
|
108
|
+
default=1,
|
|
109
|
+
help="Number of accounts to generate (1 to 100)"
|
|
110
|
+
)
|
|
111
|
+
gen_parser.add_argument(
|
|
112
|
+
"-p", "--prefix",
|
|
113
|
+
type=str,
|
|
114
|
+
default="",
|
|
115
|
+
help="Prefix the generated Session ID must start with (after 05, hex only, e.g. 'abc')"
|
|
116
|
+
)
|
|
117
|
+
gen_parser.add_argument(
|
|
118
|
+
"-t", "--threads",
|
|
119
|
+
type=int,
|
|
120
|
+
default=0,
|
|
121
|
+
help=f"Number of CPU threads to use (1 to {max_threads}, 0 for all cores, default: 0)"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
args = parser.parse_args()
|
|
125
|
+
console = Console()
|
|
126
|
+
|
|
127
|
+
if args.command == "mnemonic":
|
|
128
|
+
if args.subcommand == "verify":
|
|
129
|
+
words = args.words.strip()
|
|
130
|
+
try:
|
|
131
|
+
seed_hex = decode_mnemonic(words)
|
|
132
|
+
# Derive Session ID
|
|
133
|
+
session = Session()
|
|
134
|
+
session.set_mnemonic(words)
|
|
135
|
+
session_id = session.get_session_id()
|
|
136
|
+
console.print("[bold green]Valid Mnemonic![/bold green]")
|
|
137
|
+
console.print(f"Session ID: {session_id}")
|
|
138
|
+
console.print(f"Hex Seed: {seed_hex}")
|
|
139
|
+
except ValueError as e:
|
|
140
|
+
console.print(f"[bold red]Invalid Mnemonic:[/bold red] {e}")
|
|
141
|
+
|
|
142
|
+
elif args.subcommand == "gen":
|
|
143
|
+
words = args.words.strip() if args.words else None
|
|
144
|
+
|
|
145
|
+
if words:
|
|
146
|
+
# Derive Session ID for provided mnemonic
|
|
147
|
+
try:
|
|
148
|
+
session = Session()
|
|
149
|
+
session.set_mnemonic(words)
|
|
150
|
+
session_id = session.get_session_id()
|
|
151
|
+
console.print(f"{session_id} - {words}")
|
|
152
|
+
except ValueError as e:
|
|
153
|
+
console.print(f"[bold red]Error deriving ID:[/bold red] {e}")
|
|
154
|
+
else:
|
|
155
|
+
# Generate new accounts (vanity search or instant)
|
|
156
|
+
count = args.n
|
|
157
|
+
if count < 1 or count > 100:
|
|
158
|
+
parser.error("The number of accounts (-n) must be between 1 and 100.")
|
|
159
|
+
|
|
160
|
+
threads_arg = args.threads
|
|
161
|
+
if threads_arg < 0 or threads_arg > max_threads:
|
|
162
|
+
parser.error(f"The number of threads (-t/--threads) must be between 0 and {max_threads} (your CPU has {max_threads} cores).")
|
|
163
|
+
|
|
164
|
+
num_workers = max_threads if threads_arg == 0 else threads_arg
|
|
165
|
+
|
|
166
|
+
prefix = args.prefix.lower() if args.prefix else ""
|
|
167
|
+
if prefix:
|
|
168
|
+
if not all(c in "0123456789abcdef" for c in prefix):
|
|
169
|
+
parser.error("The prefix (-p/--prefix) must contain only valid hex characters (0-9, a-f).")
|
|
170
|
+
|
|
171
|
+
for _ in range(count):
|
|
172
|
+
if prefix:
|
|
173
|
+
start_time = time.time()
|
|
174
|
+
total_expected = 16 ** len(prefix)
|
|
175
|
+
|
|
176
|
+
# Shared resources for multiprocessing
|
|
177
|
+
result_queue = multiprocessing.Queue()
|
|
178
|
+
checked_counter = multiprocessing.Value(ctypes.c_uint64, 0)
|
|
179
|
+
stop_event = multiprocessing.Event()
|
|
180
|
+
|
|
181
|
+
# Spawn workers
|
|
182
|
+
processes = []
|
|
183
|
+
for _ in range(num_workers):
|
|
184
|
+
p = multiprocessing.Process(
|
|
185
|
+
target=search_worker,
|
|
186
|
+
args=(prefix, result_queue, checked_counter, stop_event)
|
|
187
|
+
)
|
|
188
|
+
p.daemon = True
|
|
189
|
+
p.start()
|
|
190
|
+
processes.append(p)
|
|
191
|
+
|
|
192
|
+
with Live(Text("Initializing search...", style="yellow"), refresh_per_second=10) as live:
|
|
193
|
+
while not stop_event.is_set() and result_queue.empty():
|
|
194
|
+
time.sleep(0.1)
|
|
195
|
+
checked = checked_counter.value
|
|
196
|
+
elapsed = time.time() - start_time
|
|
197
|
+
speed = checked / elapsed if elapsed > 0 else 0
|
|
198
|
+
|
|
199
|
+
if checked < total_expected:
|
|
200
|
+
remaining_checks = total_expected - checked
|
|
201
|
+
remaining_seconds = remaining_checks / speed if speed > 0 else 0
|
|
202
|
+
eta_str = format_time(remaining_seconds)
|
|
203
|
+
else:
|
|
204
|
+
avg_seconds = total_expected / speed if speed > 0 else 0
|
|
205
|
+
eta_str = f"running over (avg {format_time(avg_seconds)})"
|
|
206
|
+
|
|
207
|
+
live.update(Text(
|
|
208
|
+
f"Searching for ID starting with '05{prefix}' ({num_workers} threads)...\n"
|
|
209
|
+
f"Speed: {speed:.1f} id/sec | Checked: {checked}/{total_expected} | Time: {elapsed:.1f}s | ETA: {eta_str}",
|
|
210
|
+
style="cyan"
|
|
211
|
+
))
|
|
212
|
+
|
|
213
|
+
# Retrieve result and cleanup processes
|
|
214
|
+
session_id, mnemonic = result_queue.get()
|
|
215
|
+
for p in processes:
|
|
216
|
+
if p.is_alive():
|
|
217
|
+
p.terminate()
|
|
218
|
+
|
|
219
|
+
console.print(f"[bold green]{session_id}[/bold green] - [bold white]{mnemonic}[/bold white]")
|
|
220
|
+
else:
|
|
221
|
+
# Instant generation without prefix
|
|
222
|
+
mnemonic = Session.generate_mnemonic()
|
|
223
|
+
session = Session()
|
|
224
|
+
session.set_mnemonic(mnemonic)
|
|
225
|
+
session_id = session.get_session_id()
|
|
226
|
+
console.print(f"[bold green]{session_id}[/bold green] - [bold white]{mnemonic}[/bold white]")
|
|
227
|
+
else:
|
|
228
|
+
mnemonic_parser.print_help()
|
|
229
|
+
else:
|
|
230
|
+
parser.print_help()
|
|
231
|
+
|
|
232
|
+
if __name__ == "__main__":
|
|
233
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: session-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: A pure Python implementation of the Session Messenger protocol client
|
|
5
5
|
Author-email: Towux <towux@users.noreply.github.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/towux/session-python
|
|
@@ -51,10 +51,10 @@ This library is a 1-to-1 port of the official `@session.js` (Bun) client modules
|
|
|
51
51
|
|
|
52
52
|
## 📦 Installation
|
|
53
53
|
|
|
54
|
-
To install `session-python
|
|
54
|
+
To install `session-python`:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
pip install
|
|
57
|
+
pip install session-python
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
---
|
|
@@ -2,6 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
session_python/__init__.py
|
|
5
|
+
session_python/cli.py
|
|
5
6
|
session_python/client.py
|
|
6
7
|
session_python/crypto.py
|
|
7
8
|
session_python/mnemonic.py
|
|
@@ -10,6 +11,7 @@ session_python/polling.py
|
|
|
10
11
|
session_python.egg-info/PKG-INFO
|
|
11
12
|
session_python.egg-info/SOURCES.txt
|
|
12
13
|
session_python.egg-info/dependency_links.txt
|
|
14
|
+
session_python.egg-info/entry_points.txt
|
|
13
15
|
session_python.egg-info/requires.txt
|
|
14
16
|
session_python.egg-info/top_level.txt
|
|
15
17
|
session_python/protobuf/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|