lbenc 1.1.0__py3-none-any.whl
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.
- lbenc-1.1.0.dist-info/METADATA +64 -0
- lbenc-1.1.0.dist-info/RECORD +7 -0
- lbenc-1.1.0.dist-info/WHEEL +5 -0
- lbenc-1.1.0.dist-info/entry_points.txt +2 -0
- lbenc-1.1.0.dist-info/licenses/LICENSE +17 -0
- lbenc-1.1.0.dist-info/top_level.txt +1 -0
- lbenc.py +501 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lbenc
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Telegram file-processing bot with interactive configuration
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: telegram,bot,python,zip,source transformation
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: pyTelegramBotAPI>=4.16.1
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# LBENC
|
|
14
|
+
|
|
15
|
+
A Telegram file-processing bot with a terminal configuration menu.
|
|
16
|
+
|
|
17
|
+
## Install locally
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
python -m pip install .
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Run
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
lbenc YOUR_TELEGRAM_BOT_TOKEN
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
In an interactive terminal, the configuration menu opens first. Select `0` to save and start the bot.
|
|
30
|
+
|
|
31
|
+
To change settings later:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
lbenc --configure
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
You can also set `TELEGRAM_BOT_TOKEN` in your environment and run `lbenc`.
|
|
38
|
+
|
|
39
|
+
## Configuration options
|
|
40
|
+
|
|
41
|
+
1. Change inline button URLs
|
|
42
|
+
2. Change `/start` message (enter `/done` on its own line to finish)
|
|
43
|
+
3. Change welcome image URL
|
|
44
|
+
4. View configuration
|
|
45
|
+
5. Reset configuration
|
|
46
|
+
0. Save and continue to bot
|
|
47
|
+
|
|
48
|
+
Settings are stored in `lbenc_config.json` in the current working directory.
|
|
49
|
+
|
|
50
|
+
## PyPI publishing
|
|
51
|
+
|
|
52
|
+
The package is prepared for PyPI, but it is not published automatically by this ZIP. First verify the name is available, then build and upload with your PyPI account:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python -m pip install build twine
|
|
56
|
+
python -m build
|
|
57
|
+
python -m twine upload dist/*
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Note about the 30 methods
|
|
61
|
+
|
|
62
|
+
The 30 menu choices are text transformations and representations. Base encodings, compression, ROT13, XOR, and Caesar shifts are not secure encryption. Hash methods are one-way, and Marshal output depends on the Python version. Outputs are delivered as `.txt` because many options do not produce executable Python source.
|
|
63
|
+
|
|
64
|
+
Uploaded Python files are treated as data and are not executed by the bot.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
lbenc.py,sha256=CDiTRUGdCmxT0W7yF6hozSlKmtT7iCMFBDkeYx0O398,16923
|
|
2
|
+
lbenc-1.1.0.dist-info/licenses/LICENSE,sha256=c35Q9u0AzYFQ3NXGzTkwHpMoL-6ST-JGG-LUvC3sXqE,816
|
|
3
|
+
lbenc-1.1.0.dist-info/METADATA,sha256=ctgFK0mOy77Ts74ZfnQ_jVgjLREYIR09KDGXXD4NwlE,1770
|
|
4
|
+
lbenc-1.1.0.dist-info/WHEEL,sha256=a-udL2m4V_6Yn8b-uKzWb12RjRxwwMo6ohYFM24tpaY,91
|
|
5
|
+
lbenc-1.1.0.dist-info/entry_points.txt,sha256=G5Oz6siJin1NNFKYkQOmN882QZgRQk5Ch-xPSUZcTsQ,37
|
|
6
|
+
lbenc-1.1.0.dist-info/top_level.txt,sha256=bHWiA2dsBUnnN0npzj5llS9TuiwqiUelVJgclwAcOiw,6
|
|
7
|
+
lbenc-1.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LBENC contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lbenc
|
lbenc.py
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""LBENC — Telegram file-processing bot with an interactive configuration menu."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import io
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
import tempfile
|
|
13
|
+
import zipfile
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from threading import Lock
|
|
16
|
+
from urllib.parse import urlparse
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import telebot
|
|
20
|
+
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
|
21
|
+
except ImportError:
|
|
22
|
+
telebot = None
|
|
23
|
+
InlineKeyboardMarkup = InlineKeyboardButton = None
|
|
24
|
+
|
|
25
|
+
APP_NAME = "LBENC"
|
|
26
|
+
APP_VERSION = "1.1.0"
|
|
27
|
+
CONFIG_FILE = Path.cwd() / "lbenc_config.json"
|
|
28
|
+
MAX_FILE_SIZE = 20 * 1024 * 1024
|
|
29
|
+
STATE_LOCK = Lock()
|
|
30
|
+
USER_FILES = {}
|
|
31
|
+
|
|
32
|
+
DEFAULT_CONFIG = {
|
|
33
|
+
"channel_url": "https://t.me/",
|
|
34
|
+
"developer_url": "https://t.me/",
|
|
35
|
+
"start_message": (
|
|
36
|
+
"🛡️ Welcome to LBENC Bot!\\n\\n"
|
|
37
|
+
"Send me a file to process it.\\n"
|
|
38
|
+
"• Python files: choose a text transformation\\n"
|
|
39
|
+
"• Any file: convert it to a ZIP archive"
|
|
40
|
+
),
|
|
41
|
+
"image_url": "https://iili.io/F9VZX9f.jpg",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# These options provide text transformations/representations, not 30 secure
|
|
45
|
+
# encryption algorithms. Hashes are one-way; marshal is Python-version-specific.
|
|
46
|
+
def _b64(text):
|
|
47
|
+
import base64
|
|
48
|
+
return base64.b64encode(text.encode("utf-8")).decode("ascii")
|
|
49
|
+
|
|
50
|
+
def _b32(text):
|
|
51
|
+
import base64
|
|
52
|
+
return base64.b32encode(text.encode("utf-8")).decode("ascii")
|
|
53
|
+
|
|
54
|
+
def _b16(text):
|
|
55
|
+
import base64
|
|
56
|
+
return base64.b16encode(text.encode("utf-8")).decode("ascii")
|
|
57
|
+
|
|
58
|
+
def _zlib(text):
|
|
59
|
+
import base64, zlib
|
|
60
|
+
return base64.b64encode(zlib.compress(text.encode("utf-8"))).decode("ascii")
|
|
61
|
+
|
|
62
|
+
def _marshal(text):
|
|
63
|
+
import base64, marshal
|
|
64
|
+
return base64.b64encode(marshal.dumps(compile(text, "<lbenc>", "exec"))).decode("ascii")
|
|
65
|
+
|
|
66
|
+
def _xor(text, key):
|
|
67
|
+
return bytes(byte ^ key for byte in text.encode("utf-8")).hex()
|
|
68
|
+
|
|
69
|
+
def _caesar(text, shift):
|
|
70
|
+
return "".join(chr((ord(ch) + shift) % 0x110000) for ch in text)
|
|
71
|
+
|
|
72
|
+
def _rot13_b64(text):
|
|
73
|
+
import base64, codecs
|
|
74
|
+
return base64.b64encode(codecs.encode(text, "rot_13").encode("utf-8")).decode("ascii")
|
|
75
|
+
|
|
76
|
+
def _gzip(text):
|
|
77
|
+
import base64, gzip
|
|
78
|
+
return base64.b64encode(gzip.compress(text.encode("utf-8"))).decode("ascii")
|
|
79
|
+
|
|
80
|
+
def _bz2(text):
|
|
81
|
+
import base64, bz2
|
|
82
|
+
return base64.b64encode(bz2.compress(text.encode("utf-8"))).decode("ascii")
|
|
83
|
+
|
|
84
|
+
def _lzma(text):
|
|
85
|
+
import base64, lzma
|
|
86
|
+
return base64.b64encode(lzma.compress(text.encode("utf-8"))).decode("ascii")
|
|
87
|
+
|
|
88
|
+
def _binary(text):
|
|
89
|
+
return " ".join(format(byte, "08b") for byte in text.encode("utf-8"))
|
|
90
|
+
|
|
91
|
+
def _unicode_escape(text):
|
|
92
|
+
return text.encode("unicode_escape").decode("ascii")
|
|
93
|
+
|
|
94
|
+
def _json_string(text):
|
|
95
|
+
return json.dumps(text, ensure_ascii=True)
|
|
96
|
+
|
|
97
|
+
def _sha256(text):
|
|
98
|
+
import hashlib
|
|
99
|
+
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
|
100
|
+
|
|
101
|
+
def _sha3(text):
|
|
102
|
+
import hashlib
|
|
103
|
+
return hashlib.sha3_256(text.encode("utf-8")).hexdigest()
|
|
104
|
+
|
|
105
|
+
def _blake2(text):
|
|
106
|
+
import hashlib
|
|
107
|
+
return hashlib.blake2b(text.encode("utf-8")).hexdigest()
|
|
108
|
+
|
|
109
|
+
def _base85(text):
|
|
110
|
+
import base64
|
|
111
|
+
return base64.b85encode(text.encode("utf-8")).decode("ascii")
|
|
112
|
+
|
|
113
|
+
def _ascii85(text):
|
|
114
|
+
import base64
|
|
115
|
+
return base64.a85encode(text.encode("utf-8")).decode("ascii")
|
|
116
|
+
|
|
117
|
+
def _url_reverse(text):
|
|
118
|
+
from urllib.parse import quote
|
|
119
|
+
return quote(text, safe="")[::-1]
|
|
120
|
+
|
|
121
|
+
def _token_dump(text):
|
|
122
|
+
import io, tokenize
|
|
123
|
+
rows = []
|
|
124
|
+
for tok in tokenize.generate_tokens(io.StringIO(text).readline):
|
|
125
|
+
rows.append({
|
|
126
|
+
"type": tokenize.tok_name.get(tok.type, str(tok.type)),
|
|
127
|
+
"string": tok.string, "start": tok.start, "end": tok.end
|
|
128
|
+
})
|
|
129
|
+
return json.dumps(rows, ensure_ascii=False, indent=2)
|
|
130
|
+
|
|
131
|
+
def _ast_dump(text):
|
|
132
|
+
import ast
|
|
133
|
+
return ast.dump(ast.parse(text), indent=2, include_attributes=True)
|
|
134
|
+
|
|
135
|
+
def _compile_check(text):
|
|
136
|
+
compile(text, "<lbenc-validation>", "exec")
|
|
137
|
+
return text
|
|
138
|
+
|
|
139
|
+
def _url_encode(text):
|
|
140
|
+
from urllib.parse import quote
|
|
141
|
+
return quote(text, safe="")
|
|
142
|
+
|
|
143
|
+
def _hex(text):
|
|
144
|
+
return text.encode("utf-8").hex()
|
|
145
|
+
|
|
146
|
+
def _reverse_rot13(text):
|
|
147
|
+
import codecs
|
|
148
|
+
return codecs.encode(text[::-1], "rot_13")
|
|
149
|
+
|
|
150
|
+
def _identity(text):
|
|
151
|
+
return text
|
|
152
|
+
|
|
153
|
+
METHODS = [
|
|
154
|
+
("ROT13", lambda t: __import__("codecs").encode(t, "rot_13")),
|
|
155
|
+
("Base64", _b64),
|
|
156
|
+
("Base32", _b32),
|
|
157
|
+
("Base16", _b16),
|
|
158
|
+
("Zlib + Base64", _zlib),
|
|
159
|
+
("Marshal + Base64", _marshal),
|
|
160
|
+
("Reverse", lambda t: t[::-1]),
|
|
161
|
+
("URL Encode", _url_encode),
|
|
162
|
+
("Hex", _hex),
|
|
163
|
+
("XOR-7", lambda t: _xor(t, 7)),
|
|
164
|
+
("XOR-5", lambda t: _xor(t, 5)),
|
|
165
|
+
("Caesar-3", lambda t: _caesar(t, 3)),
|
|
166
|
+
("Caesar-5", lambda t: _caesar(t, 5)),
|
|
167
|
+
("ROT13 + Base64", _rot13_b64),
|
|
168
|
+
("Zlib + Base64 (alias)", _zlib),
|
|
169
|
+
("Marshal Base64 (alias)", _marshal),
|
|
170
|
+
("Reverse + ROT13", _reverse_rot13),
|
|
171
|
+
("URL + Reverse", _url_reverse),
|
|
172
|
+
("Identity", _identity),
|
|
173
|
+
("Base85", _base85),
|
|
174
|
+
("ASCII85", _ascii85),
|
|
175
|
+
("Gzip + Base64", _gzip),
|
|
176
|
+
("BZ2 + Base64", _bz2),
|
|
177
|
+
("LZMA + Base64", _lzma),
|
|
178
|
+
("Binary", _binary),
|
|
179
|
+
("Unicode Escape", _unicode_escape),
|
|
180
|
+
("JSON String", _json_string),
|
|
181
|
+
("SHA-256 Digest", _sha256),
|
|
182
|
+
("SHA3-256 Digest", _sha3),
|
|
183
|
+
("BLAKE2b Digest", _blake2),
|
|
184
|
+
]
|
|
185
|
+
assert len(METHODS) == 30
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def load_config():
|
|
189
|
+
config = DEFAULT_CONFIG.copy()
|
|
190
|
+
try:
|
|
191
|
+
saved = json.loads(CONFIG_FILE.read_text(encoding="utf-8"))
|
|
192
|
+
if isinstance(saved, dict):
|
|
193
|
+
for key in config:
|
|
194
|
+
if isinstance(saved.get(key), str) and saved[key].strip():
|
|
195
|
+
config[key] = saved[key].strip()
|
|
196
|
+
except (OSError, json.JSONDecodeError):
|
|
197
|
+
pass
|
|
198
|
+
return config
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def save_config(config):
|
|
202
|
+
temp_path = CONFIG_FILE.with_suffix(".tmp")
|
|
203
|
+
temp_path.write_text(
|
|
204
|
+
json.dumps(config, ensure_ascii=False, indent=2) + "\n",
|
|
205
|
+
encoding="utf-8",
|
|
206
|
+
)
|
|
207
|
+
temp_path.replace(CONFIG_FILE)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _valid_http_url(value):
|
|
211
|
+
parsed = urlparse(value.strip())
|
|
212
|
+
return parsed.scheme in ("http", "https") and bool(parsed.netloc)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _read_multiline_message():
|
|
216
|
+
print("\nEnter your /start message.")
|
|
217
|
+
print("Type /done on a line by itself when finished.")
|
|
218
|
+
lines = []
|
|
219
|
+
while True:
|
|
220
|
+
try:
|
|
221
|
+
line = input()
|
|
222
|
+
except EOFError:
|
|
223
|
+
break
|
|
224
|
+
if line.strip() == "/done":
|
|
225
|
+
break
|
|
226
|
+
lines.append(line)
|
|
227
|
+
value = "\n".join(lines).strip()
|
|
228
|
+
if not value:
|
|
229
|
+
print("⚠️ Empty message ignored.")
|
|
230
|
+
return None
|
|
231
|
+
return value
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def configure_menu():
|
|
235
|
+
config = load_config()
|
|
236
|
+
while True:
|
|
237
|
+
print("\n" + "═" * 52)
|
|
238
|
+
print(" L B E N C")
|
|
239
|
+
print(" PREMIUM CONFIGURATION")
|
|
240
|
+
print("═" * 52)
|
|
241
|
+
print(" 1. Change inline button URLs")
|
|
242
|
+
print(" 2. Change /start message")
|
|
243
|
+
print(" 3. Change welcome image URL")
|
|
244
|
+
print(" 4. View current configuration")
|
|
245
|
+
print(" 5. Reset configuration")
|
|
246
|
+
print(" 0. Save and continue to bot")
|
|
247
|
+
print("─" * 52)
|
|
248
|
+
try:
|
|
249
|
+
choice = input("Choose an option: ").strip()
|
|
250
|
+
except (EOFError, KeyboardInterrupt):
|
|
251
|
+
print("\nConfiguration cancelled.")
|
|
252
|
+
return config
|
|
253
|
+
|
|
254
|
+
if choice == "1":
|
|
255
|
+
channel = input("Please send your channel URL: ").strip()
|
|
256
|
+
if not _valid_http_url(channel):
|
|
257
|
+
print("❌ Invalid URL. Use an http:// or https:// URL.")
|
|
258
|
+
continue
|
|
259
|
+
developer = input("Please send your developer URL: ").strip()
|
|
260
|
+
if not _valid_http_url(developer):
|
|
261
|
+
print("❌ Invalid URL. Use an http:// or https:// URL.")
|
|
262
|
+
continue
|
|
263
|
+
config["channel_url"] = channel
|
|
264
|
+
config["developer_url"] = developer
|
|
265
|
+
save_config(config)
|
|
266
|
+
print("✅ URL changed successfully — changed by @hardhackar009")
|
|
267
|
+
elif choice == "2":
|
|
268
|
+
message = _read_multiline_message()
|
|
269
|
+
if message is not None:
|
|
270
|
+
config["start_message"] = message
|
|
271
|
+
save_config(config)
|
|
272
|
+
print("✅ Start message changed successfully — changed by @hardhackar009")
|
|
273
|
+
elif choice == "3":
|
|
274
|
+
image_url = input("Please send your image URL: ").strip()
|
|
275
|
+
if not _valid_http_url(image_url):
|
|
276
|
+
print("❌ Invalid URL. Use an http:// or https:// URL.")
|
|
277
|
+
continue
|
|
278
|
+
config["image_url"] = image_url
|
|
279
|
+
save_config(config)
|
|
280
|
+
print("✅ Image URL changed successfully — changed by @hardhackar009")
|
|
281
|
+
elif choice == "4":
|
|
282
|
+
print("\nCurrent configuration:")
|
|
283
|
+
print(f"Channel URL : {config['channel_url']}")
|
|
284
|
+
print(f"Developer URL : {config['developer_url']}")
|
|
285
|
+
print(f"Start message :\n{config['start_message']}")
|
|
286
|
+
print(f"Image URL : {config['image_url']}")
|
|
287
|
+
print(f"Config file : {CONFIG_FILE.resolve()}")
|
|
288
|
+
elif choice == "5":
|
|
289
|
+
confirm = input("Reset configuration to defaults? (y/N): ").strip().lower()
|
|
290
|
+
if confirm == "y":
|
|
291
|
+
config = DEFAULT_CONFIG.copy()
|
|
292
|
+
save_config(config)
|
|
293
|
+
print("✅ Configuration reset.")
|
|
294
|
+
else:
|
|
295
|
+
print("Reset cancelled.")
|
|
296
|
+
elif choice == "0":
|
|
297
|
+
save_config(config)
|
|
298
|
+
print("✅ Configuration saved.")
|
|
299
|
+
return config
|
|
300
|
+
else:
|
|
301
|
+
print("❌ Invalid option. Please choose 0–5.")
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def _cleanup_user_file(user_id):
|
|
305
|
+
with STATE_LOCK:
|
|
306
|
+
state = USER_FILES.pop(user_id, None)
|
|
307
|
+
if state:
|
|
308
|
+
try:
|
|
309
|
+
os.remove(state["path"])
|
|
310
|
+
except OSError:
|
|
311
|
+
pass
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _safe_filename(name):
|
|
315
|
+
name = os.path.basename(name or "upload.bin")
|
|
316
|
+
name = re.sub(r"[^A-Za-z0-9._ -]", "_", name).strip(" .")
|
|
317
|
+
return (name or "upload.bin")[:160]
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def create_bot(token):
|
|
321
|
+
if telebot is None:
|
|
322
|
+
raise RuntimeError(
|
|
323
|
+
"Missing dependency. Install it with: python -m pip install pyTelegramBotAPI"
|
|
324
|
+
)
|
|
325
|
+
bot = telebot.TeleBot(token)
|
|
326
|
+
|
|
327
|
+
def keyboard():
|
|
328
|
+
cfg = load_config()
|
|
329
|
+
markup = InlineKeyboardMarkup()
|
|
330
|
+
markup.row(
|
|
331
|
+
InlineKeyboardButton("📢 Channel", url=cfg["channel_url"]),
|
|
332
|
+
InlineKeyboardButton("👨💻 Developer", url=cfg["developer_url"]),
|
|
333
|
+
)
|
|
334
|
+
return markup
|
|
335
|
+
|
|
336
|
+
@bot.message_handler(commands=["start"])
|
|
337
|
+
def start_handler(message):
|
|
338
|
+
cfg = load_config()
|
|
339
|
+
try:
|
|
340
|
+
bot.send_photo(
|
|
341
|
+
message.chat.id,
|
|
342
|
+
cfg["image_url"],
|
|
343
|
+
caption=cfg["start_message"],
|
|
344
|
+
reply_markup=keyboard(),
|
|
345
|
+
)
|
|
346
|
+
except Exception:
|
|
347
|
+
bot.send_message(
|
|
348
|
+
message.chat.id,
|
|
349
|
+
cfg["start_message"],
|
|
350
|
+
reply_markup=keyboard(),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
@bot.message_handler(content_types=["document"])
|
|
354
|
+
def document_handler(message):
|
|
355
|
+
user_id = message.from_user.id
|
|
356
|
+
doc = message.document
|
|
357
|
+
if doc.file_size and doc.file_size > MAX_FILE_SIZE:
|
|
358
|
+
bot.reply_to(message, "❌ Maximum upload size is 20 MB.")
|
|
359
|
+
return
|
|
360
|
+
filename = _safe_filename(doc.file_name)
|
|
361
|
+
try:
|
|
362
|
+
info = bot.get_file(doc.file_id)
|
|
363
|
+
payload = bot.download_file(info.file_path)
|
|
364
|
+
fd, path = tempfile.mkstemp(prefix="lbenc_")
|
|
365
|
+
with os.fdopen(fd, "wb") as f:
|
|
366
|
+
f.write(payload)
|
|
367
|
+
_cleanup_user_file(user_id)
|
|
368
|
+
with STATE_LOCK:
|
|
369
|
+
USER_FILES[user_id] = {
|
|
370
|
+
"path": path,
|
|
371
|
+
"name": filename,
|
|
372
|
+
"is_python": filename.lower().endswith(".py"),
|
|
373
|
+
}
|
|
374
|
+
markup = InlineKeyboardMarkup(row_width=3)
|
|
375
|
+
if filename.lower().endswith(".py"):
|
|
376
|
+
buttons = [
|
|
377
|
+
InlineKeyboardButton(name, callback_data=f"ENC|{i}")
|
|
378
|
+
for i, (name, _) in enumerate(METHODS)
|
|
379
|
+
]
|
|
380
|
+
for offset in range(0, len(buttons), 3):
|
|
381
|
+
markup.row(*buttons[offset:offset + 3])
|
|
382
|
+
markup.row(InlineKeyboardButton("📦 Convert to ZIP", callback_data="ZIP"))
|
|
383
|
+
prompt = (
|
|
384
|
+
"Choose one of the 30 processing methods or ZIP:"
|
|
385
|
+
if filename.lower().endswith(".py")
|
|
386
|
+
else "Choose ZIP conversion:"
|
|
387
|
+
)
|
|
388
|
+
bot.send_message(message.chat.id, prompt, reply_markup=markup)
|
|
389
|
+
except Exception as exc:
|
|
390
|
+
bot.reply_to(message, f"❌ Upload failed: {type(exc).__name__}")
|
|
391
|
+
|
|
392
|
+
@bot.callback_query_handler(func=lambda call: call.data.startswith("ENC|"))
|
|
393
|
+
def transform_handler(call):
|
|
394
|
+
user_id = call.from_user.id
|
|
395
|
+
try:
|
|
396
|
+
index = int(call.data.split("|", 1)[1])
|
|
397
|
+
except (ValueError, IndexError):
|
|
398
|
+
bot.answer_callback_query(call.id, "Invalid method.")
|
|
399
|
+
return
|
|
400
|
+
if not 0 <= index < len(METHODS):
|
|
401
|
+
bot.answer_callback_query(call.id, "Unknown method.")
|
|
402
|
+
return
|
|
403
|
+
with STATE_LOCK:
|
|
404
|
+
data = USER_FILES.get(user_id)
|
|
405
|
+
if not data:
|
|
406
|
+
bot.answer_callback_query(call.id, "Please upload the file again.")
|
|
407
|
+
return
|
|
408
|
+
if not data["is_python"]:
|
|
409
|
+
bot.answer_callback_query(call.id, "Python files only.")
|
|
410
|
+
return
|
|
411
|
+
name, method = METHODS[index]
|
|
412
|
+
try:
|
|
413
|
+
text = Path(data["path"]).read_text(encoding="utf-8-sig")
|
|
414
|
+
output = method(text)
|
|
415
|
+
header = (
|
|
416
|
+
f"LBENC method: {name}\n"
|
|
417
|
+
"Note: this is a text transformation/representation, not necessarily "
|
|
418
|
+
"runnable Python. Hash outputs are one-way.\n\n"
|
|
419
|
+
)
|
|
420
|
+
bio = io.BytesIO((header + output).encode("utf-8"))
|
|
421
|
+
bio.name = Path(data["name"]).stem + f"_method_{index + 1:02d}.txt"
|
|
422
|
+
bot.send_document(
|
|
423
|
+
call.message.chat.id, bio, caption=f"✅ Processed: {name}"
|
|
424
|
+
)
|
|
425
|
+
bot.answer_callback_query(call.id, "Processing complete.")
|
|
426
|
+
except Exception as exc:
|
|
427
|
+
bot.send_message(
|
|
428
|
+
call.message.chat.id,
|
|
429
|
+
f"❌ Processing failed: {type(exc).__name__}: {exc}",
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
@bot.callback_query_handler(func=lambda call: call.data == "ZIP")
|
|
433
|
+
def zip_handler(call):
|
|
434
|
+
user_id = call.from_user.id
|
|
435
|
+
with STATE_LOCK:
|
|
436
|
+
data = USER_FILES.get(user_id)
|
|
437
|
+
if not data:
|
|
438
|
+
bot.answer_callback_query(call.id, "Please upload the file again.")
|
|
439
|
+
return
|
|
440
|
+
try:
|
|
441
|
+
out = io.BytesIO()
|
|
442
|
+
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as archive:
|
|
443
|
+
archive.write(data["path"], arcname=data["name"])
|
|
444
|
+
out.seek(0)
|
|
445
|
+
out.name = Path(data["name"]).stem + ".zip"
|
|
446
|
+
bot.send_document(
|
|
447
|
+
call.message.chat.id, out, caption=f"✅ {data['name']} converted to ZIP"
|
|
448
|
+
)
|
|
449
|
+
bot.answer_callback_query(call.id, "ZIP created.")
|
|
450
|
+
except Exception as exc:
|
|
451
|
+
bot.send_message(
|
|
452
|
+
call.message.chat.id,
|
|
453
|
+
f"❌ ZIP conversion failed: {type(exc).__name__}: {exc}",
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
return bot
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def main(argv=None):
|
|
460
|
+
parser = argparse.ArgumentParser(
|
|
461
|
+
prog="lbenc",
|
|
462
|
+
description="LBENC Telegram bot — configure URLs, start text, and image.",
|
|
463
|
+
)
|
|
464
|
+
parser.add_argument("token", nargs="?", help="Telegram bot token")
|
|
465
|
+
parser.add_argument("--configure", action="store_true", help="Open configuration menu")
|
|
466
|
+
parser.add_argument("--version", action="version", version=f"%(prog)s {APP_VERSION}")
|
|
467
|
+
args = parser.parse_args(argv)
|
|
468
|
+
|
|
469
|
+
if args.configure:
|
|
470
|
+
configure_menu()
|
|
471
|
+
return 0
|
|
472
|
+
|
|
473
|
+
token = (args.token or os.environ.get("TELEGRAM_BOT_TOKEN", "")).strip()
|
|
474
|
+
if not token:
|
|
475
|
+
print("Usage: lbenc YOUR_TELEGRAM_BOT_TOKEN")
|
|
476
|
+
print("Or configure settings with: lbenc --configure")
|
|
477
|
+
return 2
|
|
478
|
+
|
|
479
|
+
# Show the interactive menu when launched from a terminal.
|
|
480
|
+
if sys.stdin.isatty():
|
|
481
|
+
configure_menu()
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
bot = create_bot(token)
|
|
485
|
+
except RuntimeError as exc:
|
|
486
|
+
print(f"❌ {exc}")
|
|
487
|
+
return 2
|
|
488
|
+
|
|
489
|
+
print("╔══════════════════════════════════════════════╗")
|
|
490
|
+
print("║ LBENC BOT IS RUNNING ║")
|
|
491
|
+
print("╚══════════════════════════════════════════════╝")
|
|
492
|
+
print("Press Ctrl+C to stop.")
|
|
493
|
+
try:
|
|
494
|
+
bot.infinity_polling(skip_pending=True, timeout=30, long_polling_timeout=30)
|
|
495
|
+
except KeyboardInterrupt:
|
|
496
|
+
print("\nLBENC stopped.")
|
|
497
|
+
return 0
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
if __name__ == "__main__":
|
|
501
|
+
raise SystemExit(main())
|