JerryCoder commited on
Commit
b6be17f
·
verified ·
1 Parent(s): cb7ab5d

Create bot.py

Browse files
Files changed (1) hide show
  1. bot.py +219 -0
bot.py ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import sys
4
+ import zipfile
5
+ import base64
6
+ import random
7
+ import shutil
8
+ import subprocess
9
+ import tempfile
10
+ from pathlib import Path
11
+
12
+ from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
13
+ from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes, CallbackContext
14
+
15
+ from motor.motor_asyncio import AsyncIOMotorClient
16
+ from config import BOT_TOKEN, ADMIN_ID, MONGO_URI, DB_NAME
17
+
18
+ # --- MongoDB Setup ---
19
+ mongo_client = AsyncIOMotorClient(MONGO_URI)
20
+ db = mongo_client[DB_NAME]
21
+ users_col = db["users"]
22
+ bans_col = db["banned_users"]
23
+ stats_col = db["stats"]
24
+
25
+ async def init_stats():
26
+ if not await stats_col.find_one({"_id":"encryption_count"}):
27
+ await stats_col.insert_one({"_id":"encryption_count","count":0})
28
+
29
+ # --- Helpers ---
30
+ def make_zip_bytes(filename: str, content: bytes) -> bytes:
31
+ bio = io.BytesIO()
32
+ with zipfile.ZipFile(bio, "w", compression=zipfile.ZIP_DEFLATED) as zf:
33
+ zf.writestr(filename, content)
34
+ return bio.getvalue()
35
+
36
+ def rand_bytes(n): return os.urandom(n)
37
+ def bytes_to_c_array_literal(b: bytes) -> str:
38
+ return ",".join(str(x) for x in b)
39
+
40
+ def xor_string(s: str, key: int = None) -> str:
41
+ if key is None: key = random.randint(1,255)
42
+ arr = [ord(c)^key for c in s]
43
+ return f"(lambda s:''.join(chr(B^{key}) for B in s))([{','.join(str(x) for x in arr)}])"
44
+
45
+ def generate_junk_data(min_kb=50,max_kb=70):
46
+ junk_size = random.randint(min_kb*1024,max_kb*1024)
47
+ chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_()-./,;:!?"
48
+ return ''.join(random.choice(chars) for _ in range(junk_size))
49
+
50
+ # --- C Template ---
51
+ C_TEMPLATE = """/*KEY_BYTES*/""" # Replace with actual C template
52
+ SETUP_PY = """# Your setup.py content"""
53
+
54
+ def compile_c_extension(build_dir: Path):
55
+ (build_dir/"setup.py").write_text(SETUP_PY)
56
+ proc = subprocess.run([sys.executable,"setup.py","build_ext","--inplace"],
57
+ cwd=str(build_dir), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=30)
58
+ candidates = list(build_dir.glob("aes_helper*.so")) + list(build_dir.glob("build/lib.*/*.so"))
59
+ if not candidates:
60
+ raise RuntimeError("Compile fail:\n"+proc.stdout.decode())
61
+ return candidates[0]
62
+
63
+ def build_wrapper(j_b64, so_b64, so_filename):
64
+ lines = [
65
+ "wulcan = '.WulcanPy'",
66
+ "import os as O, sys as S, base64 as B, tempfile as T",
67
+ f"WL='{j_b64}'",
68
+ f"M={repr(so_b64)}",
69
+ f"D=O.path.join(T.gettempdir(),{xor_string('ninja_tmp')});O.makedirs(D,exist_ok=True)",
70
+ f"P=O.path.join(D,{xor_string(so_filename)})",
71
+ "open(P,'wb').write(B.b64decode(M))",
72
+ "import importlib.machinery as L, importlib.util as U",
73
+ "loader=L.ExtensionFileLoader('aes_helper',P);spec=U.spec_from_loader(loader.name,loader);mod=U.module_from_spec(spec);loader.exec_module(mod)",
74
+ "import sys, io, zipfile, runpy",
75
+ "try:",
76
+ "\tc=B.b64decode(WL);z=mod.decrypt(c)",
77
+ "\tt=T.mkdtemp(prefix='nin_');zipfile.ZipFile(io.BytesIO(z)).extractall(t)",
78
+ "\tS.path.insert(0,t)",
79
+ "\te=[f for f in O.listdir(t) if f.endswith('.py')][0]",
80
+ "\tep=O.path.join(t,e)",
81
+ "\tS.argv=[ep]+S.argv[1:]",
82
+ "\trunpy.run_path(ep,run_name='__main__')",
83
+ "except Exception as E: print('ninja_error',E)",
84
+ "finally: O.remove(P) if O.path.exists(P) else None",
85
+ f"JUNK={repr(generate_junk_data())}"
86
+ ]
87
+ return "\n".join(lines)
88
+
89
+ # --- Process File ---
90
+ async def process_file(input_file: Path, update: Update, context: ContextTypes.DEFAULT_TYPE):
91
+ user = update.effective_user
92
+ tmpdir = Path(tempfile.mkdtemp())
93
+ tmp_file = tmpdir/input_file.name
94
+ tmp_file.write_bytes(input_file.read_bytes())
95
+ py_bytes = tmp_file.read_bytes()
96
+ zip_b = make_zip_bytes(input_file.name, py_bytes)
97
+ key, iv = rand_bytes(32), rand_bytes(16)
98
+ c_src = C_TEMPLATE.replace("/*KEY_BYTES*/", bytes_to_c_array_literal(key)).replace("/*IV_BYTES*/", bytes_to_c_array_literal(iv))
99
+ (tmpdir/"aes_helper.c").write_text(c_src)
100
+ so_path = compile_c_extension(tmpdir)
101
+ import importlib.util
102
+ spec = importlib.util.spec_from_file_location("aes_helper",so_path)
103
+ mod = importlib.util.module_from_spec(spec)
104
+ spec.loader.exec_module(mod)
105
+ cipher = mod.encrypt(zip_b)
106
+ cipher_b64 = base64.b64encode(cipher).decode()
107
+ so_b64 = base64.b64encode(so_path.read_bytes()).decode()
108
+ wrapper = build_wrapper(cipher_b64, so_b64, so_path.name)
109
+ out_name = input_file.stem+"_enc.py"
110
+ out_path = tmpdir/out_name
111
+ out_path.write_text(wrapper)
112
+ with open(out_path,'rb') as f:
113
+ await update.message.reply_document(document=f,filename=out_name,caption=f"✅ File encoded: {out_name}")
114
+ await stats_col.update_one({"_id":"encryption_count"},{"$inc":{"count":1}})
115
+ await context.bot.send_message(chat_id=ADMIN_ID, text=f'{{"status":"file_encrypted","user":"{user.full_name}","file":"{input_file.name}"}}')
116
+ shutil.rmtree(tmpdir, ignore_errors=True)
117
+
118
+ # --- Handlers ---
119
+ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
120
+ user = update.effective_user
121
+ if not await users_col.find_one({"user_id":user.id}):
122
+ await users_col.insert_one({"user_id":user.id,"name":user.full_name,"username":user.username or "None","language":user.language_code or "Unknown"})
123
+ await context.bot.send_message(chat_id=ADMIN_ID, text=f'{{"status":"bot_started","user":"{user.full_name}"}}')
124
+ keyboard=[[InlineKeyboardButton("▶ Join Channel",url="https://t.me/kuttuxd17")]]
125
+ await update.message.reply_text("Welcome to Secure File Encryptor Bot!",reply_markup=InlineKeyboardMarkup(keyboard))
126
+
127
+ async def encode(update: Update, context: ContextTypes.DEFAULT_TYPE):
128
+ user = update.effective_user
129
+ if await bans_col.find_one({"user_id":user.id}):
130
+ await update.message.reply_text("You are banned.")
131
+ return
132
+ await update.message.reply_text("Please upload a .py file.")
133
+
134
+ async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
135
+ user = update.effective_user
136
+ if await bans_col.find_one({"user_id":user.id}):
137
+ await update.message.reply_text("You are banned.")
138
+ return
139
+ document = update.message.document
140
+ if not document.file_name.endswith(".py"):
141
+ await update.message.reply_text("Upload .py file only.")
142
+ return
143
+ file = await document.get_file()
144
+ tmpdir = Path(tempfile.mkdtemp())
145
+ input_path = tmpdir/document.file_name
146
+ await file.download_to_drive(str(input_path))
147
+ await process_file(input_path, update, context)
148
+
149
+ # --- Admin Commands ---
150
+ async def total_users(update: Update, context: ContextTypes.DEFAULT_TYPE):
151
+ if update.effective_user.id != ADMIN_ID:
152
+ await update.message.reply_text("Unauthorized")
153
+ return
154
+ count = await users_col.count_documents({})
155
+ await update.message.reply_text(f"Total users: {count}")
156
+
157
+ async def total_enc(update: Update, context: ContextTypes.DEFAULT_TYPE):
158
+ if update.effective_user.id != ADMIN_ID:
159
+ await update.message.reply_text("Unauthorized")
160
+ return
161
+ stats = await stats_col.find_one({"_id":"encryption_count"})
162
+ await update.message.reply_text(f"Total encryptions: {stats['count'] if stats else 0}")
163
+
164
+ async def broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):
165
+ if update.effective_user.id != ADMIN_ID:
166
+ await update.message.reply_text("Unauthorized")
167
+ return
168
+ msg=" ".join(context.args)
169
+ users=users_col.find({})
170
+ count=0
171
+ async for u in users:
172
+ try: await context.bot.send_message(chat_id=u['user_id'], text=msg); count+=1
173
+ except: pass
174
+ await update.message.reply_text(f"Broadcast sent to {count} users")
175
+
176
+ async def ban(update: Update, context: ContextTypes.DEFAULT_TYPE):
177
+ if update.effective_user.id != ADMIN_ID: return
178
+ try: user_id=int(context.args[0]); await bans_col.update_one({"user_id":user_id},{"$set":{"user_id":user_id}},upsert=True); await update.message.reply_text(f"Banned {user_id}")
179
+ except: await update.message.reply_text("Provide user ID")
180
+
181
+ async def unban(update: Update, context: ContextTypes.DEFAULT_TYPE):
182
+ if update.effective_user.id != ADMIN_ID: return
183
+ try: user_id=int(context.args[0]); await bans_col.delete_one({"user_id":user_id}); await update.message.reply_text(f"Unbanned {user_id}")
184
+ except: await update.message.reply_text("Provide user ID")
185
+
186
+ # --- Main ---
187
+ async def main():
188
+ await init_stats()
189
+
190
+ app = Application.builder().token(BOT_TOKEN).build()
191
+
192
+ # --- Handlers ---
193
+ app.add_handler(CommandHandler("start", start))
194
+ app.add_handler(CommandHandler("encode", encode))
195
+ app.add_handler(MessageHandler(filters.Document.ALL, handle_document))
196
+ app.add_handler(CommandHandler("totalusers", total_users))
197
+ app.add_handler(CommandHandler("totalenc", total_enc))
198
+ app.add_handler(CommandHandler("broadcast", broadcast))
199
+ app.add_handler(CommandHandler("ban", ban))
200
+ app.add_handler(CommandHandler("unban", unban))
201
+
202
+ # --- Start webhook for Hugging Face ---
203
+ WEBHOOK_URL = f"https://{os.environ.get('SPACE_DOMAIN')}/api/webhook/{BOT_TOKEN}"
204
+ await app.run_webhook(
205
+ listen="0.0.0.0",
206
+ port=int(os.environ.get("PORT", 7860)),
207
+ url_path=BOT_TOKEN,
208
+ webhook_url=WEBHOOK_URL
209
+ )
210
+
211
+ # --- Run Bot ---
212
+ if __name__ == "__main__":
213
+ import asyncio
214
+ try:
215
+ asyncio.run(main())
216
+ except RuntimeError:
217
+ # Fix for HF Spaces already running loop
218
+ loop = asyncio.get_event_loop()
219
+ loop.create_task(main())