briskwave commited on
Commit
2ddda16
·
verified ·
1 Parent(s): 5009f4e

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +2 -1
  2. index.js +180 -0
  3. package.json +16 -0
Dockerfile CHANGED
@@ -1,8 +1,9 @@
1
  FROM node:18-slim
2
 
3
- # 1. تثبيت المتصفح والخطوط العربية
4
  RUN apt-get update && apt-get install -y \
5
  chromium \
 
6
  fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
7
  --no-install-recommends \
8
  && rm -rf /var/lib/apt/lists/*
 
1
  FROM node:18-slim
2
 
3
+ # 1. تثبيت المتصفح والخطوط العربية وحل مشكلة DNS
4
  RUN apt-get update && apt-get install -y \
5
  chromium \
6
+ dnsutils \
7
  fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
8
  --no-install-recommends \
9
  && rm -rf /var/lib/apt/lists/*
index.js ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // 1. CONFIGURATION (from src/config.js)
2
+ require('dotenv').config();
3
+
4
+ const config = {
5
+ // نستخدم نموذج Qwen لأنه قوي جداً في اللغة العربية والفهم المنطقي
6
+ hfModel: "Qwen/Qwen2.5-72B-Instruct",
7
+ hfToken: process.env.HF_TOKEN, // يجب وضعه في Secrets
8
+ moroccanSites: [
9
+ 'site:anapec.org',
10
+ 'site:alwadifa-maroc.com',
11
+ 'site:marocannonces.com',
12
+ 'site:rekrute.com',
13
+ 'site:linkedin.com/jobs/morocco',
14
+ 'site:welovers.ma'
15
+ ],
16
+ puppeteerArgs: [
17
+ '--no-sandbox',
18
+ '--disable-setuid-sandbox',
19
+ '--disable-dev-shm-usage',
20
+ '--disable-gpu'
21
+ ]
22
+ };
23
+
24
+ // 2. AI AGENT (from src/ai_agent.js)
25
+ const { HfInference } = require("@huggingface/inference");
26
+ const hf = new HfInference(config.hfToken);
27
+
28
+ async function analyzeMessage(userMessage) {
29
+ const systemPrompt = `
30
+ You are an expert Moroccan Job Recruiter Agent.
31
+ User Message: "${userMessage}"
32
+
33
+ Task: Analyze the intent.
34
+ 1. If user wants a job, extract: job_title (in French/English), city.
35
+ 2. If greeting, respond in Moroccan Darija.
36
+
37
+ Output Format: You must reply with a valid JSON Object ONLY. Do not write any other text.
38
+ Schema:
39
+ {
40
+ "intent": "job_search" or "chat",
41
+ "keywords": "job title keywords",
42
+ "city": "city name",
43
+ "reply": "Short response in Darija/Arabic to send to user"
44
+ }
45
+ `;
46
+
47
+ try {
48
+ const response = await hf.chatCompletion({
49
+ model: config.hfModel,
50
+ messages: [
51
+ { role: "system", content: "You are a strict JSON output machine." },
52
+ { role: "user", content: systemPrompt }
53
+ ],
54
+ max_tokens: 500,
55
+ temperature: 0.3 // تقليل الإبداع لضمان دقة JSON
56
+ });
57
+
58
+ const content = response.choices[0].message.content;
59
+
60
+ // تنظيف النتيجة لاستخراج JSON فقط
61
+ const jsonMatch = content.match(/\{[\s\S]*\}/);
62
+ if (jsonMatch) {
63
+ return JSON.parse(jsonMatch[0]);
64
+ }
65
+ throw new Error("Failed to parse JSON");
66
+
67
+ } catch (error) {
68
+ console.error("AI Error:", error);
69
+ return {
70
+ intent: "chat",
71
+ reply: "سمح ليا، مافهمتش مزيان. عاود كتب ليا شنو بغيتي بالضبط."
72
+ };
73
+ }
74
+ }
75
+
76
+ // 3. SEARCH ENGINE (from src/search_engine.js)
77
+ const { search } = require('duck-duck-scrape');
78
+
79
+ async function findJobs(keywords, city) {
80
+ const sites = config.moroccanSites.join(' OR ');
81
+ // معادلة بحث قوية
82
+ const query = `(${sites}) ${keywords} ${city} "2024" OR "2025"`;
83
+
84
+ console.log(`🔎 Searching HF: ${query}`);
85
+
86
+ try {
87
+ const results = await search(query, {
88
+ safeSearch: "off",
89
+ time: "w" // آخر أسبوع
90
+ });
91
+
92
+ return results.results.slice(0, 3).map(r => ({
93
+ title: r.title,
94
+ link: r.url,
95
+ source: r.domain
96
+ }));
97
+ } catch (e) {
98
+ console.error("Search Error:", e);
99
+ return [];
100
+ }
101
+ }
102
+
103
+ // 4. BOT CONTROLLER (from src/bot_controller.js)
104
+ const { Client, LocalAuth } = require('whatsapp-web.js');
105
+ const qrcode = require('qrcode');
106
+
107
+ // هذا المتغير لتمرير QR Code إلى السيرفر
108
+ let qrStream = { data: null };
109
+
110
+ const client = new Client({
111
+ authStrategy: new LocalAuth({ dataPath: '/app/.wwebjs_auth' }),
112
+ puppeteer: {
113
+ headless: true,
114
+ executablePath: '/usr/bin/chromium',
115
+ args: config.puppeteerArgs
116
+ }
117
+ });
118
+
119
+ client.on('qr', (qr) => {
120
+ qrcode.toDataURL(qr, (err, url) => {
121
+ qrStream.data = url;
122
+ console.log('⚡ New QR Generated');
123
+ });
124
+ });
125
+
126
+ client.on('ready', () => {
127
+ console.log('✅ Connected to WhatsApp!');
128
+ qrStream.data = "CONNECTED";
129
+ });
130
+
131
+ client.on('message', async msg => {
132
+ if(msg.from.includes('@g.us')) return;
133
+
134
+ // 1. تحليل الرسالة عبر HF Model
135
+ const analysis = await analyzeMessage(msg.body);
136
+
137
+ // 2. الرد الأولي
138
+ if (analysis.reply) await msg.reply(analysis.reply);
139
+
140
+ // 3. البحث
141
+ if (analysis.intent === 'job_search') {
142
+ const jobs = await findJobs(analysis.keywords, analysis.city);
143
+
144
+ if (jobs.length > 0) {
145
+ let replyText = "لقيت ليك هاد العروض: 🔥\n\n";
146
+ jobs.forEach(j => {
147
+ replyText += `📌 *${j.title}*\n🔗 ${j.link}\n\n`;
148
+ });
149
+ replyText += "الله يسهل! 🤲";
150
+ await client.sendMessage(msg.from, replyText);
151
+ } else {
152
+ await client.sendMessage(msg.from, "ما لقيت والو جديد هاد السيمانة 😔");
153
+ }
154
+ }
155
+ });
156
+
157
+ client.initialize();
158
+
159
+ // 5. EXPRESS SERVER (from original index.js)
160
+ const express = require('express');
161
+ const app = express();
162
+ const port = 7860; // منفذ Hugging Face
163
+
164
+ app.get('/', (req, res) => {
165
+ if (qrStream.data === "CONNECTED") {
166
+ res.send('<h1 style="color:green;text-align:center;margin-top:20%;">✅ Bot is Connected & Running!</h1>');
167
+ } else if (qrStream.data) {
168
+ res.send(`
169
+ <div style="text-align:center;margin-top:50px;">
170
+ <h1>Scan this QR Code</h1>
171
+ <img src="${qrStream.data}" style="width:300px;"/>
172
+ <p>Refresh page if code expired.</p>
173
+ </div>
174
+ `);
175
+ } else {
176
+ res.send('<h1 style="text-align:center;">Initializing... Wait 10 seconds and Refresh 🔄</h1>');
177
+ }
178
+ });
179
+
180
+ app.listen(port, () => console.log(`Server running on port ${port}`));
package.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "morocco-hunter-bot",
3
+ "version": "2.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "start": "node index.js"
7
+ },
8
+ "dependencies": {
9
+ "whatsapp-web.js": "^1.23.0",
10
+ "qrcode": "^1.5.3",
11
+ "express": "^4.18.2",
12
+ "@huggingface/inference": "^2.6.4",
13
+ "duck-duck-scrape": "^2.2.4",
14
+ "dotenv": "^16.3.1"
15
+ }
16
+ }