Riy777 commited on
Commit
f719794
·
verified ·
1 Parent(s): a85ead0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +882 -268
app.py CHANGED
@@ -10,7 +10,7 @@ import praw
10
  import random
11
  import threading
12
  import nest_asyncio
13
- from datetime import datetime, date
14
  from email.mime.text import MIMEText
15
  from email.mime.multipart import MIMEMultipart
16
  from openai import OpenAI
@@ -18,7 +18,6 @@ from playwright.async_api import async_playwright
18
  from apscheduler.schedulers.background import BackgroundScheduler
19
  from upstash_redis import Redis as UpstashRedis
20
 
21
- # إعدادات النظام
22
  os.environ["TZ"] = "UTC"
23
  nest_asyncio.apply()
24
 
@@ -26,356 +25,971 @@ nest_asyncio.apply()
26
  # 1. إعدادات البيئة والمفاتيح
27
  # ==========================================
28
 
29
- NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
30
- GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
31
-
32
- # Reddit Keys
33
- REDDIT_CLIENT_ID = os.getenv("REDDIT_CLIENT_ID")
34
  REDDIT_CLIENT_SECRET = os.getenv("REDDIT_CLIENT_SECRET")
35
- REDDIT_USERNAME = os.getenv("REDDIT_USERNAME")
36
- REDDIT_PASSWORD = os.getenv("REDDIT_PASSWORD")
37
-
38
- # Email Keys
39
- SMTP_EMAIL = os.getenv("SMTP_EMAIL")
40
- SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
41
- SMTP_SERVER = "smtp.gmail.com"
42
- SMTP_PORT = 587
43
-
44
- # Upstash Redis Keys
45
- UPSTASH_REDIS_REST_URL = os.getenv("UPSTASH_REDIS_REST_URL")
 
 
46
  UPSTASH_REDIS_REST_TOKEN = os.getenv("UPSTASH_REDIS_REST_TOKEN")
47
 
48
- TELEGRAM_CONTACT = "@Nexusai97"
49
-
50
- # الحدود اليومية
51
  LIMITS = {
52
- "reddit_daily_comments": 50,
53
- "email_daily_send": 300,
54
- "github_daily": 999999
 
 
 
55
  }
56
 
57
  # ==========================================
58
- # 2. مدير التخزين والذاكرة (Iron Memory Manager)
59
  # ==========================================
60
 
61
- class LogManager:
 
 
 
 
 
 
 
 
 
62
  def __init__(self):
 
 
 
63
  self.enabled = False
64
  if UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN:
65
  try:
66
  self.redis = UpstashRedis(url=UPSTASH_REDIS_REST_URL, token=UPSTASH_REDIS_REST_TOKEN)
67
  self.redis.ping()
68
  self.enabled = True
69
- print("✅ Connected to Upstash Redis (Iron Memory Active)")
70
  except Exception as e:
71
- print(f"❌ Upstash Connection Error: {e}")
72
  else:
73
- print("⚠️ Upstash Credentials missing. Memory will be temporary only!")
74
 
75
- def is_processed(self, url):
76
- """فحص هل تم التعامل مع الرابط سابقاً (سحابياً)"""
77
- # أولاً نفحص الذاكرة المحلية للسرعة
78
- if url in LOCAL_STATS['processed_urls']:
79
  return True
80
-
81
- # ثانياً نفحص السحابة (للأمان بعد إعادة التشغيل)
82
  if self.enabled:
83
  try:
84
- # نستخدم أمر sismember للتحقق من وجود الرابط في المجموعة
85
- return self.redis.sismember("nexus_processed_set", url) == 1
86
  except:
87
- return False
88
  return False
89
 
90
- def mark_processed(self, url):
91
- """تسجيل الرابط بأنه تم التعامل معه"""
92
- LOCAL_STATS['processed_urls'].append(url)
93
  if self.enabled:
94
  try:
95
- # إضافة الرابط لمجموعة الروابط المعالجة
96
- self.redis.sadd("nexus_processed_set", url)
97
- except: pass
 
98
 
99
- def load_logs(self):
100
- if not self.enabled: return []
101
- try:
102
- raw_logs = self.redis.lrange("nexus_logs", 0, 99)
103
- parsed_logs = []
104
- for log in raw_logs:
105
- if isinstance(log, str):
106
- try: parsed_logs.append(json.loads(log))
107
- except: pass
108
- else: parsed_logs.append(log)
109
- return parsed_logs
110
- except: return []
111
-
112
- def save_log_entry(self, platform, title, url, original_text, our_reply):
 
 
 
 
 
113
  entry = {
114
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
115
  "platform": platform,
116
- "title": title,
117
- "url": url,
118
- "original_text": original_text[:500] + "..." if original_text else "No content",
119
- "our_reply": our_reply
120
  }
121
-
122
  LOCAL_LOGS.insert(0, entry)
123
- if len(LOCAL_LOGS) > 100: LOCAL_LOGS.pop()
124
-
125
  if self.enabled:
126
  try:
127
- self.redis.lpush("nexus_logs", json.dumps(entry))
128
- self.redis.ltrim("nexus_logs", 0, 499)
129
- except: pass
 
130
  return entry
131
 
132
- log_manager = LogManager()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
- LOCAL_STATS = {"reddit": 0, "github": 0, "email": 0, "processed_urls": []}
135
  LOCAL_LOGS = []
 
136
 
137
  # ==========================================
138
- # 3. الذكاء الاصطناعي (AI Core)
139
  # ==========================================
140
 
141
  client = OpenAI(base_url="https://integrate.api.nvidia.com/v1", api_key=NVIDIA_API_KEY)
142
 
143
- def ask_ai(prompt, system_role="You are a helpful assistant."):
144
  try:
145
- completion = client.chat.completions.create(
146
  model="meta/llama-3.3-70b-instruct",
147
- messages=[{"role": "system", "content": system_role}, {"role": "user", "content": prompt}],
148
- temperature=0.3, max_tokens=800
149
  )
150
- return completion.choices[0].message.content
151
  except Exception as e:
152
  print(f"AI Error: {e}")
153
  return None
154
 
155
- def generate_polyglot_keywords():
156
- resp = ask_ai("Generate 6 search keywords for finding people needing API, Chatbots, or AI services. Mix English, Arabic, Turkish, Chinese. Output ONLY keywords separated by commas.")
157
- if resp: return [k.strip() for k in resp.split(",") if len(k) > 3]
158
- return ["cheap api", "برمجة شات بوت", "openai alternative", "ai integration"]
159
-
160
- def generate_localized_reply(content, platform):
161
- prompt = (
162
- f"Analyze content from {platform}:\n'''{content[:800]}'''\n"
163
- "1. DETECT language.\n"
164
- "2. WRITE a short, direct reply in the SAME LANGUAGE.\n"
165
- "3. PERSONA: You are the OWNER (Individual Developer). Use 'I' (not 'We').\n"
166
- "4. TONE: Direct, Confident, Humble. (No corporate jargon).\n"
167
- f"5. TEMPLATE (Adapt to language): 'Okay, I own an API [or Chatbot service] with multiple models and high performance at a cheap price. Message me on Telegram {TELEGRAM_CONTACT}.'\n"
168
- "6. If the user asks for Chatbot, say 'I own a chatbot service'. If API, say 'I own an API'.\n"
169
- "Output ONLY the reply text."
170
- )
171
- return ask_ai(prompt)
172
-
173
- def generate_cold_email_content(website_content, company_name):
174
- prompt = (
175
- f"Company: {company_name}\nSnippet: {website_content[:500]}\n"
176
- "DETECT language. Write JSON Cold Email: { 'subject': '...', 'body': '...' }. "
177
- "Tone: Personal owner offering help. Use 'I'. "
178
- "Offer Nexus AI services (API & Chatbots). Output strict JSON."
179
- )
180
- resp = ask_ai(prompt)
181
- try:
182
- json_match = re.search(r'\{.*\}', resp, re.DOTALL)
183
- if json_match: return json.loads(json_match.group(0))
184
- except: pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  return None
186
 
187
  # ==========================================
188
- # 4. دوال التنفيذ (Executors)
189
  # ==========================================
190
 
191
- def send_smtp_email(to_email, subject, body):
192
- if not SMTP_EMAIL or not SMTP_PASSWORD: return False, "No Config"
193
- try:
194
- msg = MIMEMultipart()
195
- msg['From'] = SMTP_EMAIL
196
- msg['To'] = to_email
197
- msg['Subject'] = subject
198
- msg.attach(MIMEText(body, 'plain'))
199
- server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
200
- server.starttls()
201
- server.login(SMTP_EMAIL, SMTP_PASSWORD)
202
- server.send_message(msg)
203
- server.quit()
204
- return True, "Sent"
205
- except Exception as e: return False, str(e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  # ==========================================
208
- # 5. الحلقات (The Loops) - Protected
209
  # ==========================================
210
 
211
- # --- GitHub Loop ---
212
  def github_loop():
 
213
  while True:
214
  try:
215
- keywords = generate_polyglot_keywords()
216
- headers = {"Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json"}
217
- kw = random.choice(keywords)
218
-
219
- resp = requests.get(f"https://api.github.com/search/issues?q={kw}+state:open&sort=updated", headers=headers)
220
- if resp.status_code == 200:
221
- for item in resp.json().get("items", [])[:3]:
222
- # 🛡️ الحماية الحديدية: فحص السحابة قبل العمل
223
- if log_manager.is_processed(item['html_url']): continue
224
-
225
- reply = generate_localized_reply(item['title'] + "\n" + (item['body'] or ""), "GitHub")
226
- if reply:
227
- post_resp = requests.post(item['url']+"/comments", headers=headers, json={"body": reply})
228
- if post_resp.status_code == 201:
229
- LOCAL_STATS["github"] += 1
230
- # 🛡️ تسجيل الرابط في السحابة
231
- log_manager.mark_processed(item['html_url'])
232
- log_manager.save_log_entry("GitHub", item['title'], item['html_url'], item['body'], reply)
233
- print(f"✅ GitHub Replied: {item['title'][:20]}")
234
- time.sleep(60)
235
- time.sleep(120)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  except Exception as e:
 
237
  time.sleep(300)
238
 
239
- # --- Email Loop ---
240
- async def email_hunter_process():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  try:
242
- if LOCAL_STATS["email"] >= LIMITS["email_daily_send"]: return
243
- kw = random.choice(generate_polyglot_keywords())
244
-
245
- async with async_playwright() as p:
246
- browser = await p.chromium.launch(
247
- headless=True,
248
- args=['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  )
250
- page = await browser.new_page()
251
-
252
- try:
253
- await page.goto(f"https://duckduckgo.com/?q={kw}+email+contact&t=h_&ia=web", timeout=20000)
254
- links = await page.query_selector_all('a.result__a')
255
-
256
- extracted = []
257
- for l in links[:5]:
258
- u = await l.get_attribute('href')
259
- t = await l.inner_text()
260
- if u and "facebook" not in u and "linkedin" not in u:
261
- extracted.append((u, t))
262
-
263
- for url, title in extracted:
264
- if LOCAL_STATS["email"] >= LIMITS["email_daily_send"]: break
265
- # 🛡️ الحماية الحديدية
266
- if log_manager.is_processed(url): continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
 
268
- try:
269
- await page.goto(url, timeout=20000)
270
- content = await page.content()
271
- emails = re.findall(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", content)
272
- valid = [e for e in emails if not e.endswith(('png','jpg','js','css','svg'))]
273
-
274
- if valid:
275
- target_email = valid[0]
276
- data = generate_cold_email_content(content, title)
277
-
278
- if data:
279
- ok, msg = send_smtp_email(target_email, data['subject'], data['body'])
280
- if ok:
281
- LOCAL_STATS["email"] += 1
282
- # 🛡️ تسجيل الرابط
283
- log_manager.mark_processed(url)
284
- log_manager.save_log_entry("Email", title, url, f"Target: {target_email}", data['body'])
285
- print(f"✅ Email Sent: {title}")
286
- await asyncio.sleep(20)
287
- except: pass
288
- except: pass
289
  await browser.close()
290
- except Exception as e:
291
- print(f"Email Process Error: {e}")
292
 
293
- def run_email_loop_sync():
294
  loop = asyncio.new_event_loop()
295
  asyncio.set_event_loop(loop)
296
  while True:
297
  try:
298
- loop.run_until_complete(email_hunter_process())
299
- time.sleep(300)
300
  except Exception as e:
301
- time.sleep(60)
302
-
303
- # --- Reddit Job ---
304
- def reddit_job():
305
- try:
306
- if LOCAL_STATS["reddit"] >= LIMITS["reddit_daily_comments"]: return
307
- if not REDDIT_CLIENT_ID: return
308
-
309
- reddit = praw.Reddit(client_id=REDDIT_CLIENT_ID, client_secret=REDDIT_CLIENT_SECRET,
310
- username=REDDIT_USERNAME, password=REDDIT_PASSWORD, user_agent="NexusBot")
311
- kw = random.choice(generate_polyglot_keywords())
312
- for post in reddit.subreddit("all").search(kw, limit=3, sort="new"):
313
- # 🛡️ الحماية الحديدية
314
- if log_manager.is_processed(post.url): continue
315
-
316
- reply = generate_localized_reply(post.title + "\n" + post.selftext, "Reddit")
317
- if reply:
318
- post.reply(reply)
319
- LOCAL_STATS["reddit"] += 1
320
- # 🛡️ تسجيل الرابط
321
- log_manager.mark_processed(post.url)
322
- log_manager.save_log_entry("Reddit", post.title, post.url, post.selftext or "Image", reply)
323
- print(f"✅ Reddit Replied: {post.title[:20]}")
324
- break
325
- except: pass
326
 
327
  # ==========================================
328
- # 6. التشغيل والواجهة
329
  # ==========================================
330
 
331
- def start_background_tasks():
332
- threading.Thread(target=github_loop, daemon=True).start()
333
- threading.Thread(target=run_email_loop_sync, daemon=True).start()
334
- sched = BackgroundScheduler()
335
- sched.add_job(reddit_job, 'interval', minutes=20)
336
- sched.start()
337
- print("🚀 All Nexus Systems Operational (With Iron Memory Protection)")
 
 
 
 
 
338
 
339
- start_background_tasks()
340
 
341
- def get_dashboard_stats():
342
- return LOCAL_STATS["reddit"], LOCAL_STATS["github"], LOCAL_STATS["email"]
343
 
344
- def get_chat_history():
345
- try:
346
- logs = log_manager.load_logs()
347
- if not logs: logs = LOCAL_LOGS
348
- except:
349
- logs = LOCAL_LOGS
350
-
351
- formatted_chat = []
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  for log in logs:
353
- user_content = f"**[{log['platform']}] {log['title']}**\n\n{log.get('original_text','')}\n\n🔗 [Link]({log['url']})"
354
- bot_content = f"**🗓️ {log['timestamp']}**\n\n{log['our_reply']}"
355
- formatted_chat.append({"role": "user", "content": user_content})
356
- formatted_chat.append({"role": "assistant", "content": bot_content})
357
-
358
- return formatted_chat
359
-
360
- with gr.Blocks(title="Nexus Ultimate") as demo:
361
- gr.Markdown("# 🦅 Nexus Marketing Operations Center")
362
- gr.Markdown(f"Storage: Upstash Redis | Engine: Playwright Docker | Contact: {TELEGRAM_CONTACT}")
363
-
 
 
 
 
 
364
  with gr.Row():
365
- r = gr.Number(label="Reddit Today")
366
- g = gr.Number(label="GitHub Total")
367
- e = gr.Number(label="Emails Today")
368
-
369
- refresh_btn = gr.Button("🔄 Refresh Data & Logs")
370
-
371
- gr.Markdown("### 💬 Live Interaction Logs")
372
- chatbot_view = gr.Chatbot(label="History Log", height=600)
373
-
374
- refresh_btn.click(get_dashboard_stats, outputs=[r, g, e])
375
- refresh_btn.click(get_chat_history, outputs=[chatbot_view])
376
-
377
- demo.load(get_dashboard_stats, outputs=[r, g, e])
378
- demo.load(get_chat_history, outputs=[chatbot_view])
 
 
 
379
 
380
  if __name__ == "__main__":
381
- demo.launch(server_name="0.0.0.0", server_port=7860, theme=gr.themes.Base())
 
10
  import random
11
  import threading
12
  import nest_asyncio
13
+ from datetime import datetime, timedelta
14
  from email.mime.text import MIMEText
15
  from email.mime.multipart import MIMEMultipart
16
  from openai import OpenAI
 
18
  from apscheduler.schedulers.background import BackgroundScheduler
19
  from upstash_redis import Redis as UpstashRedis
20
 
 
21
  os.environ["TZ"] = "UTC"
22
  nest_asyncio.apply()
23
 
 
25
  # 1. إعدادات البيئة والمفاتيح
26
  # ==========================================
27
 
28
+ NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
29
+ GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
30
+ REDDIT_CLIENT_ID = os.getenv("REDDIT_CLIENT_ID")
 
 
31
  REDDIT_CLIENT_SECRET = os.getenv("REDDIT_CLIENT_SECRET")
32
+ REDDIT_USERNAME = os.getenv("REDDIT_USERNAME")
33
+ REDDIT_PASSWORD = os.getenv("REDDIT_PASSWORD")
34
+ SMTP_EMAIL = os.getenv("SMTP_EMAIL")
35
+ SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
36
+ DEVTO_API_KEY = os.getenv("DEVTO_API_KEY") # اختياري
37
+ DISCORD_BOT_TOKEN = os.getenv("DISCORD_BOT_TOKEN") # اختياري
38
+ DISCORD_GUILD_IDS = os.getenv("DISCORD_GUILD_IDS", "") # معرفات السيرفرات (مفصولة بفاصلة)
39
+ SMTP_SERVER = "smtp.gmail.com"
40
+ SMTP_PORT = 587
41
+
42
+ SITE_URL = "https://www.orgteh.com"
43
+
44
+ UPSTASH_REDIS_REST_URL = os.getenv("UPSTASH_REDIS_REST_URL")
45
  UPSTASH_REDIS_REST_TOKEN = os.getenv("UPSTASH_REDIS_REST_TOKEN")
46
 
47
+ # حدود يومية للحماية من الحظر
 
 
48
  LIMITS = {
49
+ "reddit_daily": 30,
50
+ "github_daily": 80,
51
+ "email_daily": 200,
52
+ "devto_daily": 25,
53
+ "hn_daily": 40,
54
+ "discord_daily": 20,
55
  }
56
 
57
  # ==========================================
58
+ # 2. نظام الذاكرة الحديدية (Iron Memory)
59
  # ==========================================
60
 
61
+ class IronMemory:
62
+ """
63
+ يحفظ كل URL / معرف مستخدم تم التعامل معه بشكل دائم.
64
+ يعمل على طبقتين: ذاكرة محلية (سريعة) + Upstash Redis (سحابية / دائمة).
65
+ لن نعود لأي شخص أو رابط تم حفظه أبداً حتى بعد إعادة التشغيل.
66
+ """
67
+ SET_KEY = "orgteh:processed"
68
+ LOGS_KEY = "orgteh:logs"
69
+ USERS_KEY = "orgteh:users" # مجموعة معرفات المستخدمين تحديداً
70
+
71
  def __init__(self):
72
+ self._local: set = set()
73
+ self._users: set = set()
74
+ self.redis = None
75
  self.enabled = False
76
  if UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN:
77
  try:
78
  self.redis = UpstashRedis(url=UPSTASH_REDIS_REST_URL, token=UPSTASH_REDIS_REST_TOKEN)
79
  self.redis.ping()
80
  self.enabled = True
81
+ print("✅ Iron Memory: Upstash Redis متصل الذاكرة الدائمة نشطة")
82
  except Exception as e:
83
+ print(f"❌ Upstash خطأ: {e} — سيعمل النظام بذاكرة مؤقتة فقط")
84
  else:
85
+ print("⚠️ بيانات Upstash غير موجودة الذاكرة مؤقتة فقط")
86
 
87
+ # --- فحص ---
88
+ def seen(self, key: str) -> bool:
89
+ if key in self._local:
 
90
  return True
 
 
91
  if self.enabled:
92
  try:
93
+ return bool(self.redis.sismember(self.SET_KEY, key))
 
94
  except:
95
+ pass
96
  return False
97
 
98
+ def user_seen(self, user_id: str) -> bool:
99
+ if user_id in self._users:
100
+ return True
101
  if self.enabled:
102
  try:
103
+ return bool(self.redis.sismember(self.USERS_KEY, user_id))
104
+ except:
105
+ pass
106
+ return False
107
 
108
+ # --- تسجيل ---
109
+ def mark(self, key: str):
110
+ self._local.add(key)
111
+ if self.enabled:
112
+ try:
113
+ self.redis.sadd(self.SET_KEY, key)
114
+ except:
115
+ pass
116
+
117
+ def mark_user(self, user_id: str):
118
+ self._users.add(user_id)
119
+ if self.enabled:
120
+ try:
121
+ self.redis.sadd(self.USERS_KEY, user_id)
122
+ except:
123
+ pass
124
+
125
+ # --- السجلات ---
126
+ def log(self, platform, title, url, snippet, reply):
127
  entry = {
128
+ "ts": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
129
  "platform": platform,
130
+ "title": title,
131
+ "url": url,
132
+ "snippet": (snippet or "")[:400],
133
+ "reply": reply,
134
  }
 
135
  LOCAL_LOGS.insert(0, entry)
136
+ if len(LOCAL_LOGS) > 200:
137
+ LOCAL_LOGS.pop()
138
  if self.enabled:
139
  try:
140
+ self.redis.lpush(self.LOGS_KEY, json.dumps(entry))
141
+ self.redis.ltrim(self.LOGS_KEY, 0, 999)
142
+ except:
143
+ pass
144
  return entry
145
 
146
+ def load_logs(self):
147
+ if self.enabled:
148
+ try:
149
+ raw = self.redis.lrange(self.LOGS_KEY, 0, 199)
150
+ result = []
151
+ for item in raw:
152
+ try:
153
+ result.append(json.loads(item) if isinstance(item, str) else item)
154
+ except:
155
+ pass
156
+ return result
157
+ except:
158
+ pass
159
+ return LOCAL_LOGS[:]
160
+
161
 
162
+ memory = IronMemory()
163
  LOCAL_LOGS = []
164
+ LOCAL_STATS = {"reddit": 0, "github": 0, "email": 0, "devto": 0, "hn": 0, "discord": 0}
165
 
166
  # ==========================================
167
+ # 3. محرك الذكاء الاصطناعي
168
  # ==========================================
169
 
170
  client = OpenAI(base_url="https://integrate.api.nvidia.com/v1", api_key=NVIDIA_API_KEY)
171
 
172
+ def ask_ai(prompt: str, system: str = "You are a helpful assistant.", temp=0.5) -> str | None:
173
  try:
174
+ resp = client.chat.completions.create(
175
  model="meta/llama-3.3-70b-instruct",
176
+ messages=[{"role": "system", "content": system}, {"role": "user", "content": prompt}],
177
+ temperature=temp, max_tokens=900
178
  )
179
+ return resp.choices[0].message.content.strip()
180
  except Exception as e:
181
  print(f"AI Error: {e}")
182
  return None
183
 
184
+ # ==========================================
185
+ # 4. نظام كلمات البحث الذكي تلقائي 100%
186
+ # ==========================================
187
+ # طبقتان:
188
+ # 1. FALLBACK_KEYWORDS — كلمات ثابتة لضمان العمل دائماً
189
+ # 2. كل KEYWORD_REFRESH_MINUTES يطلب النموذج توليد كلمات جديدة بنفسه
190
+ # بكل اللغات الممكنة بناءً على ما يعرفه عن Orgteh وسوق AI API
191
+
192
+ # ── كلمات ثابتة (Fallback) — تُستخدم عند بدء التشغيل أو فشل التوليد ──
193
+ FALLBACK_KEYWORDS: list[str] = [
194
+ # EN
195
+ "llm api", "ai api", "chatbot api", "language model api",
196
+ "integrate llm", "openai api", "deepseek api", "mistral api",
197
+ "llama api", "ai api developer", "chat completions api",
198
+ # AR
199
+ "api ذكاء اصطناعي", "دمج نموذج لغوي", "api شات بوت",
200
+ # TR
201
+ "yapay zeka api", "llm api entegrasyonu",
202
+ # FR
203
+ "api intelligence artificielle", "intégrer llm api",
204
+ # ES
205
+ "api inteligencia artificial", "integrar llm api",
206
+ # DE
207
+ "ki api", "sprachmodell api",
208
+ # PT
209
+ "api inteligência artificial", "chatbot api integração",
210
+ # RU
211
+ "языковая модель api", "llm api интеграция",
212
+ # ZH
213
+ "大语言模型 api", "ai api 接入",
214
+ # JA
215
+ "llm api 統合", "言語モデル api",
216
+ # KO
217
+ "llm api 통합", "챗봇 api 연동",
218
+ # HI
219
+ "ai api इंटीग्रेशन", "chatbot api बनाना",
220
+ # ID
221
+ "api kecerdasan buatan", "chatbot api developer",
222
+ ]
223
+
224
+ KEYWORD_REFRESH_MINUTES = 90 # كل 90 دقيقة يطلب كلمات جديدة
225
+ _keyword_cache: list[str] = list(FALLBACK_KEYWORDS)
226
+ _last_keyword_refresh: float = 0.0
227
+ _keyword_lock = threading.Lock()
228
+
229
+
230
+ def _ai_generate_keywords() -> list[str]:
231
+ """
232
+ يطلب من النموذج توليد كلمات بحث جديدة بنفسه —
233
+ بكل اللغات والزوايا الممكنة لإيجاد المطورين المحتاجين لـ AI API.
234
+ """
235
+ prompt = """You are helping market Orgteh (orgteh.com) — an AI API service for developers.
236
+ Orgteh provides: OpenAI-compatible LLM API, multiple models (DeepSeek, Mistral, Llama, Kimi, Gemma), cheap pricing.
237
+ Target: developers and startups who want to USE or ACCESS an AI language model API for their projects.
238
+
239
+ Generate 40 short search keywords (2-5 words each) to find these people on GitHub, Reddit, forums, and the web.
240
+ - Cover MANY languages: English, Arabic, Turkish, French, Spanish, German, Portuguese, Russian, Chinese, Japanese, Korean, Hindi, Indonesian, Italian, Polish, Dutch, Vietnamese, Thai, and others you know.
241
+ - Include different angles: building chatbots, integrating LLMs, looking for API access, seeking cheaper alternatives, starting AI projects.
242
+ - Keep each keyword SHORT and natural (as someone would actually search or post).
243
+ - Output ONLY a JSON array of strings, no explanation:
244
+ ["keyword1", "keyword2", ...]"""
245
+
246
+ resp = ask_ai(prompt, temp=0.8)
247
+ if resp:
248
+ try:
249
+ m = re.search(r'\[.*\]', resp, re.DOTALL)
250
+ if m:
251
+ keywords = json.loads(m.group(0))
252
+ valid = [k.strip() for k in keywords if isinstance(k, str) and 2 < len(k.strip()) < 60]
253
+ if len(valid) >= 10:
254
+ print(f"🔄 Keywords refreshed: {len(valid)} new keywords generated by AI")
255
+ return valid
256
+ except Exception as e:
257
+ print(f"Keyword generation parse error: {e}")
258
+ return []
259
+
260
+
261
+ def refresh_keywords_if_needed():
262
+ """يجدد كلمات البحث تلقائياً كل KEYWORD_REFRESH_MINUTES."""
263
+ global _keyword_cache, _last_keyword_refresh
264
+ now = time.time()
265
+ with _keyword_lock:
266
+ if now - _last_keyword_refresh > KEYWORD_REFRESH_MINUTES * 60:
267
+ new_kws = _ai_generate_keywords()
268
+ if new_kws:
269
+ # نمزج الكلمات الجديدة مع الثابتة لضمان التغطية
270
+ _keyword_cache = list(set(FALLBACK_KEYWORDS + new_kws))
271
+ _last_keyword_refresh = now
272
+
273
+
274
+ def _keyword_refresh_loop():
275
+ """خيط مستقل يجدد الكلمات بشكل دوري."""
276
+ # توليد فوري عند البدء
277
+ refresh_keywords_if_needed()
278
+ while True:
279
+ time.sleep(KEYWORD_REFRESH_MINUTES * 60)
280
+ refresh_keywords_if_needed()
281
+
282
+
283
+ def get_random_keywords(n: int = 3) -> list[str]:
284
+ """يختار n كلمات عشوائية من الكاش الحالي (ثابتة + مولّدة)."""
285
+ with _keyword_lock:
286
+ pool = _keyword_cache if _keyword_cache else FALLBACK_KEYWORDS
287
+ return random.sample(pool, min(n, len(pool)))
288
+
289
+ # ==========================================
290
+ # 5. مولّد الردود الذكي
291
+ # ==========================================
292
+
293
+ SITE_CONTEXT = """
294
+ Orgteh (orgteh.com) is an AI API service offering:
295
+ - OpenAI-compatible endpoint: https://orgteh.com/v1/chat/completions
296
+ - Multiple models: DeepSeek-R1, Kimi (256K context), Mistral, Llama-3B, Gemma
297
+ - API keys start with "Orgteh_"
298
+ - Features: GPU-accelerated, sub-second response, enterprise security, streaming support
299
+ - Extra tools: web scraper, OCR, embeddings, finance/news aggregator, AI Code Builder
300
+ - Cheaper than OpenAI with the same integration format (drop-in replacement, zero code changes)
301
+ - Registration: orgteh.com/register
302
+ """
303
+
304
+ REPLY_STYLES = [
305
+ "casual_developer", # مطور يشارك تجربته
306
+ "helpful_peer", # زميل يساعد
307
+ "been_there", # شخص مر بنفس المشكلة
308
+ ]
309
+
310
+ def ai_judge(text: str) -> bool:
311
+ """
312
+ النموذج نفسه يحكم: هل هذا الشخص يحتاج فعلاً لـ API نماذج ذكاء اصطناعي؟
313
+ لا كلمات مفتاحية — فهم النية الحقيقية فقط.
314
+ """
315
+ prompt = f"""Read this post/issue carefully:
316
+ \"\"\"
317
+ {text[:600]}
318
+ \"\"\"
319
+
320
+ Answer with ONE word only — YES or NO:
321
+ Is this person actively trying to USE or ACCESS an AI language model API
322
+ (to build something, integrate it, or get API access for a real project)?
323
+
324
+ Rules:
325
+ - YES: they are building/integrating/seeking API access to an AI model for a concrete use case.
326
+ - NO: discussing AI concepts/news/opinions, reporting unrelated bugs, academic questions, or anything not about actually using a model API.
327
+ - If unsure → NO.
328
+
329
+ Answer:"""
330
+
331
+ resp = ask_ai(prompt, temp=0.0)
332
+ if resp:
333
+ return resp.strip().upper().startswith("YES")
334
+ return False
335
+
336
+ def generate_reply(content: str, platform: str) -> str | None:
337
+ """يولد رد يبدو كمستخدم طبيعي اكتشف orgteh وأراد مشاركته."""
338
+ style = random.choice(REPLY_STYLES)
339
+ prompt = f"""
340
+ You are a real developer who uses Orgteh (orgteh.com) for AI API access.
341
+ You are browsing {platform} and found this post/issue that matches your past experience.
342
+ You want to genuinely help by sharing what worked for you.
343
+
344
+ CONTEXT ABOUT ORGTEH:
345
+ {SITE_CONTEXT}
346
+
347
+ POST/ISSUE CONTENT:
348
+ \"\"\"
349
+ {content[:700]}
350
+ \"\"\"
351
+
352
+ YOUR PERSONA STYLE TODAY: {style}
353
+
354
+ STRICT RULES:
355
+ 1. Detect the language of the post and reply in THE SAME LANGUAGE.
356
+ 2. Write as a REAL PERSON, NOT a company. Use "I" not "we".
357
+ 3. Sound natural and conversational. NOT salesy or promotional.
358
+ 4. Keep it SHORT (3-6 sentences max).
359
+ 5. Mention orgteh.com naturally as "I've been using orgteh.com" or "stumbled on orgteh.com" etc.
360
+ 6. Only reference features that are actually relevant to their specific problem.
361
+ 7. DO NOT start with "Hi" or "Hello" or generic openers.
362
+ 8. DO NOT use bullet points or formatting.
363
+ 9. Output ONLY the reply text, nothing else.
364
+
365
+ Examples of good natural openers (adapt to language/situation):
366
+ - "Been using orgteh.com's API for this exact use case, works well with Python..."
367
+ - "I built something similar, ended up using orgteh.com — they have an OpenAI-compatible endpoint so..."
368
+ - "For LLM API access orgteh.com has been solid for me, multiple models under one endpoint..."
369
+ - "جربت orgteh.com لنفس الغرض، API متوافق مع OpenAI ويشتغل مباشرة..."
370
+ - "عندي تجربة مع هذا، استخدمت orgteh.com وفيه عدة نماذج بـ endpoint موحد..."
371
+ """
372
+ return ask_ai(prompt, temp=0.7)
373
+
374
+ def generate_email(company: str, snippet: str) -> dict | None:
375
+ """يولد إيميل بارد احترافي وشخصي لاستهداف الشركات والمطورين."""
376
+ prompt = f"""
377
+ You are a developer who uses Orgteh API and wants to recommend it to a company/developer who seems to need AI API services.
378
+
379
+ COMPANY/DEV NAME: {company}
380
+ WHAT THEY DO (from their website): {snippet[:500]}
381
+
382
+ CONTEXT ABOUT ORGTEH:
383
+ {SITE_CONTEXT}
384
+
385
+ Write a cold email as a fellow developer (not a company rep).
386
+ - Detect language from the snippet and write in the SAME LANGUAGE.
387
+ - Subject: short, specific to their use case.
388
+ - Body: personal, 4-6 sentences. Reference what they do specifically.
389
+ - Sound like you're sharing a tool that helped you, not selling.
390
+ - Include orgteh.com naturally.
391
+ - Use "I" not "we".
392
+
393
+ Output STRICT JSON only:
394
+ {{"subject": "...", "body": "..."}}
395
+ """
396
+ resp = ask_ai(prompt, temp=0.6)
397
+ if resp:
398
+ try:
399
+ m = re.search(r'\{.*\}', resp, re.DOTALL)
400
+ if m:
401
+ return json.loads(m.group(0))
402
+ except:
403
+ pass
404
  return None
405
 
406
  # ==========================================
407
+ # 6. نظام مانع الحظر (Anti-Ban Engine)
408
  # ==========================================
409
 
410
+ class AntiBan:
411
+ """
412
+ يتحكم في توقيت الإرسال لكل منصة لتجنب الحظر.
413
+ يستخدم تأخيرات عشوائية وحدوداً يومية صارمة.
414
+ """
415
+ def __init__(self):
416
+ self._day = datetime.utcnow().date()
417
+ self._counts = {k: 0 for k in LIMITS}
418
+ self._lock = threading.Lock()
419
+
420
+ def _reset_if_new_day(self):
421
+ today = datetime.utcnow().date()
422
+ if today != self._day:
423
+ self._day = today
424
+ self._counts = {k: 0 for k in LIMITS}
425
+
426
+ def can_act(self, platform: str) -> bool:
427
+ key = f"{platform}_daily"
428
+ with self._lock:
429
+ self._reset_if_new_day()
430
+ return self._counts.get(key, 0) < LIMITS.get(key, 999)
431
+
432
+ def record(self, platform: str):
433
+ key = f"{platform}_daily"
434
+ with self._lock:
435
+ self._counts[key] = self._counts.get(key, 0) + 1
436
+ LOCAL_STATS[platform] = LOCAL_STATS.get(platform, 0) + 1
437
+
438
+ @staticmethod
439
+ def human_delay(base=15, jitter=20):
440
+ """تأخير عشوائي يشبه السلوك البشري."""
441
+ delay = base + random.uniform(0, jitter)
442
+ time.sleep(delay)
443
+
444
+ @staticmethod
445
+ def micro_delay():
446
+ time.sleep(random.uniform(2, 6))
447
+
448
+
449
+ antiban = AntiBan()
450
 
451
  # ==========================================
452
+ # 7. منصات البث - GitHub
453
  # ==========================================
454
 
 
455
  def github_loop():
456
+ headers = {"Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json"}
457
  while True:
458
  try:
459
+ if not GITHUB_TOKEN:
460
+ time.sleep(600)
461
+ continue
462
+
463
+ keywords = get_random_keywords(2)
464
+ for kw in keywords:
465
+ if not antiban.can_act("github"):
466
+ break
467
+
468
+ # البحث في Issues
469
+ resp = requests.get(
470
+ f"https://api.github.com/search/issues"
471
+ f"?q={requests.utils.quote(kw)}+state:open+type:issue"
472
+ f"&sort=updated&per_page=5",
473
+ headers=headers, timeout=15
474
+ )
475
+ if resp.status_code != 200:
476
+ if resp.status_code == 403:
477
+ time.sleep(300)
478
+ break
479
+
480
+ for item in resp.json().get("items", []):
481
+ url = item["html_url"]
482
+ body_text = (item.get("body") or "")
483
+ full_text = item["title"] + " " + body_text
484
+
485
+ if memory.seen(url):
486
+ continue
487
+
488
+ user_id = f"gh:{item.get('user', {}).get('login', '')}"
489
+ if memory.user_seen(user_id):
490
+ continue
491
+
492
+ # النموذج يحكم على النية — لا شروط كلمية
493
+ if not ai_judge(full_text):
494
+ print(f" ⏭ GitHub skip (not relevant): {item['title'][:40]}")
495
+ continue
496
+
497
+ reply = generate_reply(full_text, "GitHub")
498
+ if not reply:
499
+ continue
500
+
501
+ post = requests.post(
502
+ item["url"] + "/comments",
503
+ headers=headers,
504
+ json={"body": reply},
505
+ timeout=10
506
+ )
507
+ if post.status_code == 201:
508
+ memory.mark(url)
509
+ memory.mark_user(user_id)
510
+ memory.log("GitHub", item["title"], url, body_text, reply)
511
+ antiban.record("github")
512
+ print(f"✅ GitHub: {item['title'][:50]}")
513
+ antiban.human_delay(60, 60)
514
+
515
+ time.sleep(random.randint(90, 180))
516
+
517
  except Exception as e:
518
+ print(f"GitHub Error: {e}")
519
  time.sleep(300)
520
 
521
+ # ==========================================
522
+ # 8. منصات البث - Reddit
523
+ # ==========================================
524
+
525
+ # سابريدتات مناسبة فقط
526
+ TARGET_SUBREDDITS = [
527
+ "MachineLearning", "LocalLLaMA", "learnmachinelearning",
528
+ "artificial", "ChatGPT", "OpenAI", "SideProject",
529
+ "startups", "webdev", "learnprogramming", "Python",
530
+ "programming", "ArtificialIntelligence",
531
+ ]
532
+
533
+ def reddit_loop():
534
+ while True:
535
+ try:
536
+ if not REDDIT_CLIENT_ID or not antiban.can_act("reddit"):
537
+ time.sleep(600)
538
+ continue
539
+
540
+ reddit = praw.Reddit(
541
+ client_id=REDDIT_CLIENT_ID, client_secret=REDDIT_CLIENT_SECRET,
542
+ username=REDDIT_USERNAME, password=REDDIT_PASSWORD,
543
+ user_agent="orgteh-community-bot/1.0"
544
+ )
545
+
546
+ kw = random.choice(get_random_keywords(1))
547
+ sub = random.choice(TARGET_SUBREDDITS)
548
+
549
+ for post in reddit.subreddit(sub).search(kw, limit=5, sort="new"):
550
+ if not antiban.can_act("reddit"):
551
+ break
552
+
553
+ full_text = post.title + " " + (post.selftext or "")
554
+ if not ai_judge(full_text):
555
+ continue
556
+
557
+ url = post.url
558
+ user_id = f"rd:{post.author}"
559
+ if memory.seen(url) or memory.user_seen(user_id):
560
+ continue
561
+
562
+ reply = generate_reply(full_text, "Reddit")
563
+ if not reply:
564
+ continue
565
+
566
+ post.reply(reply)
567
+ memory.mark(url)
568
+ memory.mark_user(user_id)
569
+ memory.log("Reddit", post.title, url, post.selftext or "", reply)
570
+ antiban.record("reddit")
571
+ print(f"✅ Reddit: {post.title[:50]}")
572
+ antiban.human_delay(120, 180)
573
+ break # رد واحد لكل دورة
574
+
575
+ except Exception as e:
576
+ print(f"Reddit Error: {e}")
577
+ time.sleep(random.randint(900, 1500))
578
+
579
+ # ==========================================
580
+ # 9. منصات البث - Hacker News
581
+ # ==========================================
582
+
583
+ def hn_loop():
584
+ """Hacker News Ask HN / Show HN عبر Algolia API المجاني."""
585
+ base = "https://hn.algolia.com/api/v1/search"
586
+ while True:
587
+ try:
588
+ if not antiban.can_act("hn"):
589
+ time.sleep(600)
590
+ continue
591
+
592
+ kw = random.choice(SEED_KEYWORDS_EN[:8])
593
+ resp = requests.get(base, params={
594
+ "query": kw, "tags": "ask_hn,story",
595
+ "hitsPerPage": 5, "numericFilters": "points>5"
596
+ }, timeout=15)
597
+
598
+ if resp.status_code != 200:
599
+ time.sleep(300)
600
+ continue
601
+
602
+ for hit in resp.json().get("hits", []):
603
+ story_id = str(hit.get("objectID"))
604
+ full_text = (hit.get("title","") + " " + (hit.get("story_text") or ""))
605
+
606
+ if not ai_judge(full_text):
607
+ continue
608
+
609
+ url = f"https://news.ycombinator.com/item?id={story_id}"
610
+ user_id = f"hn:{hit.get('author','')}"
611
+ if memory.seen(url) or memory.user_seen(user_id):
612
+ continue
613
+
614
+ # HN لا يسمح بالتعليق عبر API — نسجل فقط للمراجعة اليدوية
615
+ # لكن نولد الرد للاطلاع
616
+ reply = generate_reply(full_text, "HackerNews")
617
+ if reply:
618
+ memory.mark(url)
619
+ memory.mark_user(user_id)
620
+ memory.log("HackerNews (Manual)", hit.get("title",""), url, full_text[:300], reply)
621
+ antiban.record("hn")
622
+ print(f"📝 HN (للمراجعة): {hit.get('title','')[:50]}")
623
+ antiban.micro_delay()
624
+
625
+ except Exception as e:
626
+ print(f"HN Error: {e}")
627
+ time.sleep(random.randint(600, 900))
628
+
629
+ # ==========================================
630
+ # 10. منصات البث - Dev.to
631
+ # ==========================================
632
+
633
+ def devto_loop():
634
+ """Dev.to API - التعليق على مقالات المطورين."""
635
+ headers = {"api-key": DEVTO_API_KEY or "", "Content-Type": "application/json"}
636
+ while True:
637
+ try:
638
+ if not DEVTO_API_KEY or not antiban.can_act("devto"):
639
+ time.sleep(1800)
640
+ continue
641
+
642
+ for tag in ["api", "llm", "openai", "chatbot", "artificialintelligence"]:
643
+ resp = requests.get(
644
+ f"https://dev.to/api/articles?tag={tag}&top=1&per_page=5",
645
+ timeout=10
646
+ )
647
+ if resp.status_code != 200:
648
+ continue
649
+
650
+ for article in resp.json():
651
+ full_text = article.get("title","") + " " + (article.get("description") or "")
652
+ if not ai_judge(full_text):
653
+ continue
654
+
655
+ url = article.get("url","")
656
+ user_id = f"devto:{article.get('user',{}).get('username','')}"
657
+ if not url or memory.seen(url) or memory.user_seen(user_id):
658
+ continue
659
+
660
+ reply = generate_reply(full_text, "Dev.to")
661
+ if not reply:
662
+ continue
663
+
664
+ post = requests.post(
665
+ f"https://dev.to/api/comments",
666
+ headers=headers,
667
+ json={"body_markdown": reply, "article_id": article["id"]},
668
+ timeout=10
669
+ )
670
+ if post.status_code in (200, 201):
671
+ memory.mark(url)
672
+ memory.mark_user(user_id)
673
+ memory.log("Dev.to", article["title"], url, full_text, reply)
674
+ antiban.record("devto")
675
+ print(f"✅ Dev.to: {article['title'][:50]}")
676
+ antiban.human_delay(90, 90)
677
+ break
678
+
679
+ except Exception as e:
680
+ print(f"Dev.to Error: {e}")
681
+ time.sleep(random.randint(1800, 2700))
682
+
683
+ # ==========================================
684
+ # 11. منصات البث - Discord (قراءة فقط → رد عبر DM)
685
+ # ==========================================
686
+
687
+ def discord_monitor_loop():
688
+ """
689
+ يراقب سيرفرات Discord المحددة ويبحث عن رسائل عن API.
690
+ يرسل DM للأشخاص المحتاجين (ليس رد عام للسيرفر).
691
+ يتطلب: DISCORD_BOT_TOKEN + DISCORD_GUILD_IDS
692
+ """
693
+ if not DISCORD_BOT_TOKEN:
694
+ return
695
+
696
+ headers = {
697
+ "Authorization": f"Bot {DISCORD_BOT_TOKEN}",
698
+ "Content-Type": "application/json"
699
+ }
700
+ guild_ids = [g.strip() for g in DISCORD_GUILD_IDS.split(",") if g.strip()]
701
+
702
+ while True:
703
+ try:
704
+ if not antiban.can_act("discord"):
705
+ time.sleep(3600)
706
+ continue
707
+
708
+ for guild_id in guild_ids:
709
+ # جلب قنوات السيرفر
710
+ ch_resp = requests.get(
711
+ f"https://discord.com/api/v10/guilds/{guild_id}/channels",
712
+ headers=headers, timeout=10
713
+ )
714
+ if ch_resp.status_code != 200:
715
+ continue
716
+
717
+ text_channels = [c for c in ch_resp.json() if c.get("type") == 0]
718
+ for channel in text_channels[:5]:
719
+ # جلب آخر الرسائل
720
+ msg_resp = requests.get(
721
+ f"https://discord.com/api/v10/channels/{channel['id']}/messages?limit=20",
722
+ headers=headers, timeout=10
723
+ )
724
+ if msg_resp.status_code != 200:
725
+ continue
726
+
727
+ for msg in msg_resp.json():
728
+ content = msg.get("content", "")
729
+ msg_id = msg.get("id", "")
730
+ user = msg.get("author", {})
731
+ user_id = f"dc:{user.get('id','')}"
732
+
733
+ if user.get("bot"):
734
+ continue
735
+ if not ai_judge(content):
736
+ continue
737
+ if memory.seen(msg_id) or memory.user_seen(user_id):
738
+ continue
739
+
740
+ # إرسال DM
741
+ dm_resp = requests.post(
742
+ "https://discord.com/api/v10/users/@me/channels",
743
+ headers=headers,
744
+ json={"recipient_id": user["id"]},
745
+ timeout=10
746
+ )
747
+ if dm_resp.status_code != 200:
748
+ continue
749
+
750
+ dm_channel = dm_resp.json().get("id")
751
+ reply = generate_reply(content, "Discord")
752
+ if not reply:
753
+ continue
754
+
755
+ send = requests.post(
756
+ f"https://discord.com/api/v10/channels/{dm_channel}/messages",
757
+ headers=headers,
758
+ json={"content": reply},
759
+ timeout=10
760
+ )
761
+ if send.status_code in (200, 201):
762
+ memory.mark(msg_id)
763
+ memory.mark_user(user_id)
764
+ memory.log("Discord DM", channel.get("name",""), msg_id, content, reply)
765
+ antiban.record("discord")
766
+ print(f"✅ Discord DM → {user.get('username','')}")
767
+ antiban.human_delay(120, 120)
768
+ break
769
+
770
+ except Exception as e:
771
+ print(f"Discord Error: {e}")
772
+ time.sleep(random.randint(3600, 5400))
773
+
774
+ # ==========================================
775
+ # 12. نظام الإيميل المتقدم
776
+ # ==========================================
777
+
778
+ def send_email(to: str, subject: str, body: str) -> tuple[bool, str]:
779
+ if not SMTP_EMAIL or not SMTP_PASSWORD:
780
+ return False, "SMTP غير مهيأ"
781
  try:
782
+ msg = MIMEMultipart()
783
+ msg["From"] = SMTP_EMAIL
784
+ msg["To"] = to
785
+ msg["Subject"] = subject
786
+ msg.attach(MIMEText(body, "plain", "utf-8"))
787
+ srv = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
788
+ srv.starttls()
789
+ srv.login(SMTP_EMAIL, SMTP_PASSWORD)
790
+ srv.send_message(msg)
791
+ srv.quit()
792
+ return True, "تم الإرسال"
793
+ except Exception as e:
794
+ return False, str(e)
795
+
796
+ # استراتيجيات البحث عن الإيميلات والشركات الناشئة
797
+ EMAIL_SEARCH_QUERIES = [
798
+ # يبني تطبيق على AI API
799
+ "site:github.io ai api project contact email",
800
+ "site:github.com llm api integration project contact",
801
+ '"llm api" developer project contact email',
802
+ '"ai api" developer "contact" OR "email" site:github.com',
803
+ # يبحث عن كيفية الاستخدام
804
+ '"how to use llm api" developer email',
805
+ '"integrate ai model" project developer contact',
806
+ # شركات ناشئة تبني على AI
807
+ "site:producthunt.com ai chatbot api developer 2024",
808
+ "site:producthunt.com llm powered app developer",
809
+ "startup ai api integration developer email contact",
810
+ "site:crunchbase.com ai startup llm api developer",
811
+ # مطورون مستقلون
812
+ "site:indie.hackers.com ai api llm project",
813
+ '"building with llm" developer email contact',
814
+ '"ai powered" app developer "reach me" OR "contact"',
815
+ # بحث عام
816
+ '"llm api" OR "ai api" developer startup email contact 2024',
817
+ '"chat completions" developer project contact email',
818
+ ]
819
+
820
+ async def email_hunter():
821
+ """يبحث عن مطورين وشركات ناشئة تحتاج فعلاً لـ API ذكاء اصطناعي."""
822
+ if not antiban.can_act("email"):
823
+ return
824
+
825
+ query = random.choice(EMAIL_SEARCH_QUERIES)
826
+
827
+ async with async_playwright() as p:
828
+ browser = await p.chromium.launch(headless=True, args=[
829
+ "--no-sandbox", "--disable-setuid-sandbox",
830
+ "--disable-dev-shm-usage", "--disable-gpu"
831
+ ])
832
+ page = await browser.new_page()
833
+ await page.set_extra_http_headers({"Accept-Language": "en-US,en;q=0.9"})
834
+
835
+ try:
836
+ # البحث في DuckDuckGo
837
+ await page.goto(
838
+ f"https://duckduckgo.com/?q={requests.utils.quote(query)}&t=h_&ia=web",
839
+ timeout=25000
840
  )
841
+ await asyncio.sleep(3)
842
+
843
+ links = await page.query_selector_all("a.result__a")
844
+ targets = []
845
+ for l in links[:8]:
846
+ href = await l.get_attribute("href")
847
+ text = await l.inner_text()
848
+ if href and all(x not in href for x in ["facebook", "linkedin", "twitter", "youtube"]):
849
+ targets.append((href, text.strip()))
850
+
851
+ for url, title in targets:
852
+ if not antiban.can_act("email"):
853
+ break
854
+ if memory.seen(url):
855
+ continue
856
+
857
+ try:
858
+ await page.goto(url, timeout=20000)
859
+ await asyncio.sleep(2)
860
+ content = await page.content()
861
+
862
+ # استخراج الإيميلات
863
+ emails = list(set(re.findall(
864
+ r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}", content
865
+ )))
866
+ emails = [e for e in emails if not any(
867
+ e.endswith(x) for x in [".png", ".jpg", ".js", ".css", ".svg", ".gif"]
868
+ ) and "example" not in e and "noreply" not in e]
869
+
870
+ if not emails:
871
+ continue
872
+
873
+ # التحقق من الصلة بالموضوع
874
+ page_text = re.sub(r"<[^>]+>", " ", content)
875
+ if not ai_judge(page_text[:2000]):
876
+ continue
877
+
878
+ target_email = emails[0]
879
+ email_id = f"email:{target_email}"
880
+ if memory.user_seen(email_id):
881
+ continue
882
+
883
+ # توليد الإيميل
884
+ company_name = title or url.split("/")[2] if "/" in url else url
885
+ data = generate_email(company_name, page_text[:600])
886
+ if not data:
887
+ continue
888
+
889
+ ok, msg = send_email(target_email, data["subject"], data["body"])
890
+ if ok:
891
+ memory.mark(url)
892
+ memory.mark_user(email_id)
893
+ memory.log("Email", title, url, f"→ {target_email}", data["body"])
894
+ antiban.record("email")
895
+ print(f"✅ Email → {target_email} [{title[:30]}]")
896
+ await asyncio.sleep(random.uniform(30, 60))
897
+
898
+ except Exception as e:
899
+ print(f" Email sub-error: {e}")
900
 
901
+ except Exception as e:
902
+ print(f"Email search error: {e}")
903
+ finally:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
  await browser.close()
 
 
905
 
906
+ def email_loop_sync():
907
  loop = asyncio.new_event_loop()
908
  asyncio.set_event_loop(loop)
909
  while True:
910
  try:
911
+ loop.run_until_complete(email_hunter())
 
912
  except Exception as e:
913
+ print(f"Email Loop Error: {e}")
914
+ time.sleep(random.randint(300, 600))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
915
 
916
  # ==========================================
917
+ # 13. تشغيل الخيوط
918
  # ==========================================
919
 
920
+ def start_all():
921
+ threads = [
922
+ threading.Thread(target=_keyword_refresh_loop, daemon=True), # مولّد الكلمات التلقائي
923
+ threading.Thread(target=github_loop, daemon=True),
924
+ threading.Thread(target=reddit_loop, daemon=True),
925
+ threading.Thread(target=hn_loop, daemon=True),
926
+ threading.Thread(target=devto_loop, daemon=True),
927
+ threading.Thread(target=discord_monitor_loop, daemon=True),
928
+ threading.Thread(target=email_loop_sync, daemon=True),
929
+ ]
930
+ for t in threads:
931
+ t.start()
932
 
933
+ print("🚀 Orgteh Marketing Engine — جميع الأنظمة تعمل")
934
 
935
+ start_all()
 
936
 
937
+ # ==========================================
938
+ # 14. واجهة Gradio
939
+ # ==========================================
940
+
941
+ def refresh_stats():
942
+ with _keyword_lock:
943
+ kw_count = len(_keyword_cache)
944
+ last_refresh = datetime.utcfromtimestamp(_last_keyword_refresh).strftime("%H:%M UTC") if _last_keyword_refresh else "لم يتم بعد"
945
+ return (
946
+ LOCAL_STATS.get("reddit", 0),
947
+ LOCAL_STATS.get("github", 0),
948
+ LOCAL_STATS.get("email", 0),
949
+ LOCAL_STATS.get("devto", 0),
950
+ LOCAL_STATS.get("hn", 0),
951
+ LOCAL_STATS.get("discord", 0),
952
+ f"{kw_count} كلمة | آخر تجديد: {last_refresh}",
953
+ )
954
+
955
+ def refresh_logs():
956
+ logs = memory.load_logs()
957
+ chat = []
958
  for log in logs:
959
+ user_msg = (
960
+ f"**[{log['platform']}]** {log['title']}\n\n"
961
+ f"{log.get('snippet','')}\n\n"
962
+ f"🔗 {log['url']}"
963
+ )
964
+ bot_msg = f"🕐 {log['ts']}\n\n{log['reply']}"
965
+ chat.append({"role": "user", "content": user_msg})
966
+ chat.append({"role": "assistant", "content": bot_msg})
967
+ return chat
968
+
969
+ with gr.Blocks(title="Orgteh Marketing Engine", theme=gr.themes.Base()) as demo:
970
+ gr.Markdown("""
971
+ # 🚀 Orgteh Marketing Engine
972
+ **موقع:** [orgteh.com](https://www.orgteh.com) | **المنصات:** GitHub · Reddit · HackerNews · Dev.to · Discord · Email
973
+ """)
974
+
975
  with gr.Row():
976
+ r_reddit = gr.Number(label="Reddit اليوم", min_width=100)
977
+ r_github = gr.Number(label="GitHub اليوم", min_width=100)
978
+ r_email = gr.Number(label="Emails اليوم", min_width=100)
979
+ r_devto = gr.Number(label="Dev.to اليوم", min_width=100)
980
+ r_hn = gr.Number(label="HN (للمراجعة)", min_width=100)
981
+ r_discord = gr.Number(label="Discord DMs", min_width=100)
982
+
983
+ r_keywords = gr.Textbox(label="🔑 كلمات البحث الحالية (AI مولّدة + ثابتة)", interactive=False)
984
+
985
+ refresh_btn = gr.Button("🔄 تحديث البيانات")
986
+ gr.Markdown("### 💬 سجل التفاعلات الحية")
987
+ log_view = gr.Chatbot(label="السجل", height=650, type="messages")
988
+
989
+ refresh_btn.click(refresh_stats, outputs=[r_reddit, r_github, r_email, r_devto, r_hn, r_discord, r_keywords])
990
+ refresh_btn.click(refresh_logs, outputs=[log_view])
991
+ demo.load(refresh_stats, outputs=[r_reddit, r_github, r_email, r_devto, r_hn, r_discord, r_keywords])
992
+ demo.load(refresh_logs, outputs=[log_view])
993
 
994
  if __name__ == "__main__":
995
+ demo.launch(server_name="0.0.0.0", server_port=7860)