xdragxt commited on
Commit
10d17d3
Β·
verified Β·
1 Parent(s): 4e5d378

Create jarvis.py

Browse files
Files changed (1) hide show
  1. jarvis.py +279 -0
jarvis.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import asyncio
4
+ import sys
5
+ import importlib.util
6
+ from pyrogram import Client, filters
7
+ from pyrogram.enums import ParseMode
8
+ import google.generativeai as genai
9
+
10
+ # check line 93 .. accordingly
11
+ # === CONFIG ===
12
+ API_ID = os.environ.get("API_ID") # Replace with your API ID
13
+ API_HASH = os.environ.get("API_HASH") # Replace with your API HASH
14
+ BOT_TOKEN = os.environ.get("BOT_TOKEN")
15
+ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
16
+ if not BOT_TOKEN:
17
+ raise RuntimeError("BOT_TOKEN environment variable not set!")
18
+ if not GEMINI_API_KEY:
19
+ raise RuntimeError("GEMINI_API_KEY environment variable not set!")
20
+
21
+ # === SETUP ===
22
+ genai.configure(api_key=GEMINI_API_KEY)
23
+ model = genai.GenerativeModel("gemini-2.5-flash")
24
+ bot = Client("JarvisBot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN)
25
+ os.makedirs("modules", exist_ok=True)
26
+
27
+ # === MODULE LOADER ===
28
+ def load_modules(folder="modules"):
29
+ for file in os.listdir(folder):
30
+ if file.endswith(".py"):
31
+ path = os.path.join(folder, file)
32
+ name = file[:-3]
33
+ spec = importlib.util.spec_from_file_location(name, path)
34
+ mod = importlib.util.module_from_spec(spec)
35
+ spec.loader.exec_module(mod)
36
+
37
+ load_modules()
38
+
39
+ # === HELPERS ===
40
+ def extract_module_name(code: str) -> str:
41
+ match = re.search(r"def\\s+([a-zA-Z_][a-zA-Z0-9_]*)", code)
42
+ return match.group(1).lower() if match else f"mod_{os.urandom(2).hex()}"
43
+
44
+ def extract_commands(code: str) -> list:
45
+ return re.findall(r'filters\\.command\\(["\'](\\w+)["\']', code)
46
+
47
+ def determine_intent(text: str) -> str:
48
+ lowered = text.lower()
49
+ if any(x in lowered for x in ["create", "make", "build"]):
50
+ return "CREATE"
51
+ elif any(x in lowered for x in ["edit", "modify"]):
52
+ return "EDIT"
53
+ elif "recode" in lowered:
54
+ return "RECODE"
55
+ else:
56
+ return "UNSURE"
57
+
58
+ def clean_code_blocks(code: str) -> str:
59
+ if code.startswith("```python"):
60
+ code = code[9:]
61
+ if code.startswith("```"):
62
+ code = code[3:]
63
+ if code.endswith("```"):
64
+ code = code[:-3]
65
+ return code.strip()
66
+
67
+ def extract_module_name_from_description(description: str) -> str:
68
+ # Try to extract the last noun/phrase as the module name
69
+ # Remove common verbs and stopwords
70
+ import re
71
+ stopwords = [
72
+ 'jarvis', 'make', 'create', 'build', 'generate', 'a', 'an', 'the', 'please', 'module', 'for', 'me', 'to', 'with', 'add', 'new', 'of', 'system', 'bot', 'command', 'that', 'and', 'in', 'on', 'by', 'as', 'is', 'it', 'my', 'this', 'do', 'can', 'you', 'i', 'want', 'need', 'from', 'like', 'using', 'feature', 'function', 'implement', 'write', 'code', 'file', 'python', 'telegram', 'pyrogram', 'handler', 'example', 'mod', 'mod_' # etc.
73
+ ]
74
+ # Remove punctuation
75
+ desc = re.sub(r'[^a-zA-Z0-9_\s]', '', description.lower())
76
+ # Remove stopwords
77
+ words = [w for w in desc.split() if w not in stopwords]
78
+ if not words:
79
+ return f"mod_{os.urandom(2).hex()}"
80
+ # Join remaining words with underscores
81
+ name = '_'.join(words)
82
+ # Remove leading/trailing underscores and collapse multiple underscores
83
+ name = re.sub(r'_+', '_', name).strip('_')
84
+ # Fallback if name is empty
85
+ if not name:
86
+ return f"mod_{os.urandom(2).hex()}"
87
+ return name
88
+
89
+ async def restart_bot(chat_id):
90
+ await bot.send_message(chat_id, "♻️ Restarting to apply new module...")
91
+ await bot.stop()
92
+ os.execl(sys.executable, sys.executable, *sys.argv)
93
+ os._exit(0) # Ensure process exits if execl fails
94
+
95
+ # === WORD TRIGGER ===
96
+ @bot.on_message(filters.private & filters.text)
97
+ async def jarvis_trigger(client, message):
98
+ text = message.text.strip()
99
+ if not text.lower().startswith("jarvis"):
100
+ return
101
+
102
+ description = text[6:].strip()
103
+ if not description:
104
+ return await message.reply("πŸ€– Hello, what would you like me to do?")
105
+
106
+ intent = determine_intent(description)
107
+ progress_msg = await message.reply(f"πŸ€– Acknowledged.\n🧠 Determining intent...\nπŸ“˜ Intent: {intent}\nπŸ“ Task: `{description}`", parse_mode=ParseMode.MARKDOWN)
108
+
109
+ # --- NEW: Handle EDIT intent for existing files ---
110
+ if intent == "EDIT":
111
+ # Try to extract filename and edit instruction
112
+ match = re.match(r"edit\s+([\w/\\.]+)\s+(.*)", description, re.IGNORECASE)
113
+ if match:
114
+ file_path = match.group(1)
115
+ edit_instruction = match.group(2)
116
+ if not os.path.exists(file_path):
117
+ await progress_msg.edit(f"❌ File `{file_path}` not found.")
118
+ return
119
+ with open(file_path, "r", encoding="utf-8") as f:
120
+ current_content = f.read()
121
+ edit_prompt = (
122
+ f"You are an expert Python developer. Here is the current content of `{file_path}`:\n"
123
+ f"""\n{current_content}\n"""
124
+ f"Please edit this file to: {edit_instruction}.\n"
125
+ f"Output ONLY the new Python code, no explanations or markdown."
126
+ )
127
+ try:
128
+ response = model.generate_content(edit_prompt)
129
+ new_code = clean_code_blocks(response.text.strip())
130
+ if not new_code or "def " not in new_code:
131
+ await progress_msg.edit(f"❌ Edit failed: No valid code returned.")
132
+ return
133
+ with open(file_path, "w", encoding="utf-8") as f:
134
+ f.write(new_code)
135
+ # Try to reload the module if it's in modules/
136
+ if file_path.startswith("modules/") and file_path.endswith(".py"):
137
+ mod_name = os.path.basename(file_path)[:-3]
138
+ try:
139
+ spec = importlib.util.spec_from_file_location(mod_name, file_path)
140
+ mod = importlib.util.module_from_spec(spec)
141
+ mod.bot = bot
142
+ spec.loader.exec_module(mod)
143
+ except Exception as test_error:
144
+ await progress_msg.edit(f"⚠️ Edit applied, but module reload failed: {test_error}")
145
+ return
146
+ await progress_msg.edit(f"βœ… Edit applied to `{file_path}`. Restarting bot...")
147
+ await asyncio.sleep(2)
148
+ asyncio.create_task(restart_bot(message.chat.id))
149
+ return
150
+ except Exception as e:
151
+ await progress_msg.edit(f"❌ Edit failed: `{str(e)[:100]}...`")
152
+ return
153
+ else:
154
+ await progress_msg.edit("❗ Could not parse edit command. Use: 'edit <file> <instruction>'")
155
+ return
156
+ # --- END NEW ---
157
+
158
+ success = False
159
+ code = ""
160
+ for attempt in range(1, 6):
161
+ await progress_msg.edit(f"`Attempt {attempt}/5:` Thinking and generating code...\n- Executing prerequisite shell commands...")
162
+
163
+ try:
164
+ prompt = (
165
+ f"Write a full Pyrogram Telegram bot module that implements:\n"
166
+ f"{description}.\n\n"
167
+ f"IMPORTANT RULES:\n"
168
+ f"1. Use 'bot' variable (not 'Client') - it will be injected\n"
169
+ f"2. Include commands using @bot.on_message(filters.command(...))\n"
170
+ f"3. Import only what you need from pyrogram\n and use 'from ..t1 import bot'\n 'try: from ..t1 import bot except (ImportError, ValueError): \n from __main__ import bot' \n"
171
+ f"4. Don't create a new Client instance\n"
172
+ f"5. Make functions async when using bot methods\n\n"
173
+ f"Output ONLY Python code, no explanations or markdown."
174
+ )
175
+ if attempt > 1:
176
+ prompt += f"\n\nNote: Previous attempt failed with error: {str(e)}. Fix that issue this time."
177
+
178
+ response = model.generate_content(prompt)
179
+ code = clean_code_blocks(response.text.strip())
180
+
181
+ if "def " not in code or "@bot" not in code:
182
+ await progress_msg.edit(f"❌ Attempt {attempt}: Invalid function or handler.")
183
+ continue
184
+
185
+ # --- NEW: Use file path from description if present ---
186
+ import re
187
+ file_path_match = re.search(r"(modules/[\w\-]+\.py)", description)
188
+ if file_path_match:
189
+ mod_path = file_path_match.group(1)
190
+ mod_name = os.path.basename(mod_path)[:-3]
191
+ else:
192
+ mod_name = extract_module_name_from_description(description)
193
+ mod_path = f"modules/{mod_name}.py"
194
+ # If file exists, use edit flow instead of creating a new file
195
+ if os.path.exists(mod_path):
196
+ with open(mod_path, "r", encoding="utf-8") as f:
197
+ current_content = f.read()
198
+ edit_prompt = (
199
+ f"You are an expert Python developer. Here is the current content of `{mod_path}`:\n"
200
+ f"""\n{current_content}\n"""
201
+ f"Please update this file to: {description}.\n"
202
+ f"Output ONLY the new Python code, no explanations or markdown."
203
+ )
204
+ response = model.generate_content(edit_prompt)
205
+ code = clean_code_blocks(response.text.strip())
206
+ # --- END NEW ---
207
+
208
+ with open(mod_path, "w", encoding="utf-8") as f:
209
+ f.write(code)
210
+
211
+ await progress_msg.edit(f"`Attempt {attempt}/5: Thinking and generating code...\n- Executing prerequisite shell commands...\n- Testing generated code...")
212
+
213
+ try:
214
+ spec = importlib.util.spec_from_file_location(mod_name, mod_path)
215
+ mod = importlib.util.module_from_spec(spec)
216
+ mod.bot = bot
217
+ spec.loader.exec_module(mod)
218
+ except Exception as test_error:
219
+ raise RuntimeError(f"Testing error: {test_error}")
220
+
221
+ await progress_msg.edit(f"`Attempt {attempt}/5:` Thinking and generating code...\n- Executing prerequisite shell commands...\n- Testing generated code...\n- Tests passed. Writing files...\n- Files written. Initiating restart...")
222
+ await asyncio.sleep(2)
223
+ asyncio.create_task(restart_bot(message.chat.id))
224
+ success = True
225
+ break
226
+
227
+ except Exception as e:
228
+ await progress_msg.edit(f"❌ Attempt {attempt} Error: `{str(e)[:100]}...`")
229
+
230
+ if not success:
231
+ await progress_msg.edit("❌ All 5 attempts failed. Please try again with a simpler instruction.")
232
+
233
+ # === /modules ===
234
+ @bot.on_message(filters.command("modules") & filters.private)
235
+ async def list_modules(client, message):
236
+ modules = [f for f in os.listdir("modules") if f.endswith(".py")]
237
+ if modules:
238
+ module_list = "\n".join([f"β€’ {m[:-3]}" for m in modules])
239
+ await message.reply(f" Loaded Modules: \n{module_list}", parse_mode=ParseMode.MARKDOWN)
240
+ else:
241
+ await message.reply(" No modules found.", parse_mode=ParseMode.MARKDOWN)
242
+
243
+ # === /delete ===
244
+ @bot.on_message(filters.command("delete") & filters.private)
245
+ async def delete_module(client, message):
246
+ if len(message.command) < 2:
247
+ return await message.reply("❗ Specify module name.\n\nExample: `/delete calculator`", parse_mode=ParseMode.MARKDOWN)
248
+
249
+ mod_name = message.command[1].lower()
250
+ mod_path = f"modules/{mod_name}.py"
251
+
252
+ if os.path.exists(mod_path):
253
+ os.remove(mod_path)
254
+ await message.reply(f"πŸ—‘ Deleted `{mod_name}.py`\n♻️ Restart required to take effect.", parse_mode=ParseMode.MARKDOWN)
255
+ else:
256
+ await message.reply(f"❌ Module `{mod_name}.py` not found.", parse_mode=ParseMode.MARKDOWN)
257
+
258
+ # === /what to do ===
259
+ @bot.on_message(filters.regex(r"(?i)what( can)? i do\??") & filters.private)
260
+ async def what_to_do(_, message):
261
+ await message.reply(
262
+ "🧠 Jarvis Assistant**\n\n"
263
+ "I can generate custom bot modules for you!\n\n"
264
+ "Commands:\n"
265
+ "`jarvis make a calculator` - Natural language trigger\n"
266
+ "`/modules` - List all modules\n"
267
+ "`/delete <name>` - Delete a module\n\n"
268
+ "**Examples:**\n"
269
+ "`jarvis build a reminder system`\n"
270
+ "`jarvis create a dice game`\n"
271
+ "`jarvis weather checker`\n",
272
+ parse_mode=ParseMode.MARKDOWN
273
+ )
274
+
275
+ # === START ===
276
+ print("πŸš€ Starting Jarvis Bot...")
277
+ load_modules()
278
+ print("βœ… Bot is ready!")
279
+ bot.run()