social-agent-cli 1.4.0 → 1.6.0

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.
package/ai/mapper.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
- import { readFileSync, writeFileSync, existsSync, mkdtempSync, rmSync } from "node:fs";
2
+ import { readFileSync, writeFileSync, existsSync, mkdtempSync, rmSync, readdirSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
  import { PATHS } from "../core/config.js";
@@ -21,7 +21,7 @@ const SELECTOR_MAP_SCHEMA = JSON.stringify({
21
21
  items: {
22
22
  type: "object",
23
23
  properties: {
24
- action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto"] },
24
+ action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto", "upload"] },
25
25
  description: { type: "string" },
26
26
  selector: { type: "string" },
27
27
  value: { type: "string" },
@@ -168,32 +168,52 @@ function claude(opts: ClaudeOptions): Promise<any> {
168
168
  });
169
169
  }
170
170
 
171
- // ── Map CRUD ─────────────────────────────────────────────────
171
+ // ── Map CRUD (platform başına tek dosya) ─────────────────────
172
+
173
+ interface PlatformMaps {
174
+ [action: string]: SelectorMap;
175
+ }
176
+
177
+ function loadPlatformFile(platform: string): PlatformMaps {
178
+ const mapPath = join(PATHS.maps, `${platform}.json`);
179
+ if (!existsSync(mapPath)) return {};
180
+ try {
181
+ return JSON.parse(readFileSync(mapPath, "utf-8"));
182
+ } catch {
183
+ return {};
184
+ }
185
+ }
186
+
187
+ function savePlatformFile(platform: string, maps: PlatformMaps): void {
188
+ const mapPath = join(PATHS.maps, `${platform}.json`);
189
+ writeFileSync(mapPath, JSON.stringify(maps, null, 2));
190
+ }
172
191
 
173
192
  export function loadMap(platform: string, action = "post"): SelectorMap | null {
174
- const mapPath = join(PATHS.maps, `${platform}_${action}.json`);
175
- if (!existsSync(mapPath)) return null;
176
- return JSON.parse(readFileSync(mapPath, "utf-8"));
193
+ const maps = loadPlatformFile(platform);
194
+ return maps[action] || null;
177
195
  }
178
196
 
179
197
  export function saveMap(map: SelectorMap): void {
180
198
  const action = map.action || "post";
181
- const mapPath = join(PATHS.maps, `${map.platform}_${action}.json`);
182
- writeFileSync(mapPath, JSON.stringify(map, null, 2));
199
+ const maps = loadPlatformFile(map.platform);
200
+ maps[action] = map;
201
+ savePlatformFile(map.platform, maps);
183
202
  }
184
203
 
185
204
  export function listMaps(platform?: string): SelectorMap[] {
186
- const { readdirSync } = require("node:fs");
187
- const maps: SelectorMap[] = [];
205
+ const results: SelectorMap[] = [];
188
206
  try {
189
207
  for (const file of readdirSync(PATHS.maps)) {
190
208
  if (!file.endsWith(".json")) continue;
191
- if (platform && !file.startsWith(platform + "_")) continue;
192
- const map = JSON.parse(readFileSync(join(PATHS.maps, file), "utf-8"));
193
- maps.push(map);
209
+ if (platform && file !== `${platform}.json`) continue;
210
+ const maps = JSON.parse(readFileSync(join(PATHS.maps, file), "utf-8"));
211
+ for (const map of Object.values(maps) as SelectorMap[]) {
212
+ results.push(map);
213
+ }
194
214
  }
195
215
  } catch {}
196
- return maps;
216
+ return results;
197
217
  }
198
218
 
199
219
  // ── Map Oluştur ──────────────────────────────────────────────
@@ -209,11 +229,13 @@ export async function generateMap(
209
229
 
210
230
  GÖREV: "${taskDescription}"
211
231
 
212
- DOM:
213
- ${domSnapshot.substring(0, 50000)}
232
+ Sayfanın Accessibility Tree'si (ARIA Snapshot):
233
+ ${domSnapshot}
214
234
 
215
235
  Kurallar:
216
- - data-testid, aria-label, role, id tercih et
236
+ - ARIA snapshot'taki role ve name bilgilerini kullanarak selector oluştur
237
+ - aria-label, role, data-testid, id tercih et
238
+ - Dosya/görsel/video yükleme için "upload" action kullan (click DEĞİL). Upload action hidden input[type=file]'a dosyayı programatik olarak set eder, file picker açmaz. value alanına {{IMAGE}} yaz.
217
239
  - Her adım için 2+ fallback selector
218
240
  - Parametreler için placeholder kullan: {{CONTENT}} = metin, {{USERNAME}} = kullanıcı adı, {{URL}} = link, vs.
219
241
  - Hangi parametreleri kullandığını "parameters" alanında listele
@@ -260,7 +282,7 @@ const REMAINING_STEPS_SCHEMA = JSON.stringify({
260
282
  items: {
261
283
  type: "object",
262
284
  properties: {
263
- action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto"] },
285
+ action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto", "upload"] },
264
286
  description: { type: "string" },
265
287
  selector: { type: "string" },
266
288
  value: { type: "string" },
@@ -311,15 +333,16 @@ Başarısız olan adım (#${failedStepIndex + 1}):
311
333
  Kalan orijinal adımlar:
312
334
  ${remainingOriginal.map((s, i) => ` ${failedStepIndex + i + 1}. [${s.action}] ${s.description} → ${s.selector}`).join("\n")}
313
335
 
314
- Güncel DOM:
315
- ${domSnapshot.substring(0, 50000)}
336
+ Sayfanın Accessibility Tree'si:
337
+ ${domSnapshot}
316
338
 
317
339
  GÖREV: Ekranın mevcut durumunu analiz et. Amaca ulaşmak için kalan adımları yeniden planla.
318
340
  - Belki selector değişmiş → yeni selector bul
319
341
  - Belki ekstra bir adım gerekiyor (popup kapatma, scroll, vb.)
320
342
  - Belki farklı bir yol izlenmeli
321
343
  - {{CONTENT}} = post metni placeholder'ı
322
- - data-testid, aria-label, role tercih et, her adıma 2+ fallback selector ver`;
344
+ - data-testid, aria-label, role tercih et, her adıma 2+ fallback selector ver
345
+ - Dosya yükleme için "upload" action kullan (click DEĞİL). value: {{IMAGE}}`;
323
346
 
324
347
  console.log(`\n[heal] AI ekranı analiz ediyor (effort: ${effort})...\n`);
325
348
 
package/ai/planner.ts CHANGED
@@ -59,9 +59,18 @@ export function listAllMaps(platform?: string): SelectorMap[] {
59
59
  try {
60
60
  for (const file of readdirSync(PATHS.maps)) {
61
61
  if (!file.endsWith(".json")) continue;
62
- if (platform && !file.startsWith(platform + "_")) continue;
63
- const map = JSON.parse(readFileSync(join(PATHS.maps, file), "utf-8"));
64
- maps.push(map);
62
+ if (platform && file !== `${platform}.json`) continue;
63
+ const content = JSON.parse(readFileSync(join(PATHS.maps, file), "utf-8"));
64
+ // Yeni format: { "post": {...}, "post_with_image": {...} }
65
+ if (content.steps) {
66
+ // Eski format: direkt map objesi
67
+ maps.push(content);
68
+ } else {
69
+ // Yeni format: action bazlı obje
70
+ for (const map of Object.values(content) as SelectorMap[]) {
71
+ if (map.steps) maps.push(map);
72
+ }
73
+ }
65
74
  }
66
75
  } catch {}
67
76
  return maps;
package/ai/runner.ts CHANGED
@@ -160,35 +160,7 @@ export async function runCommand(
160
160
  const healScreenshot = join(PATHS.screenshots, `${platform}_heal_${stepIdx}.png`);
161
161
  await page.screenshot({ path: healScreenshot });
162
162
 
163
- const domSnap = await page.evaluate(`(function() {
164
- var SKIP = {"script":1,"style":1,"noscript":1,"link":1,"meta":1};
165
- function ser(el, d, b) {
166
- if (d > 15 || b.c <= 0) return "";
167
- var t = el.tagName.toLowerCase();
168
- if (SKIP[t]) return "";
169
- if (t === "svg") return "<svg/>";
170
- var st = window.getComputedStyle(el);
171
- if (st.display === "none" || st.visibility === "hidden") return "";
172
- var at = [];
173
- for (var i = 0; i < el.attributes.length; i++) {
174
- var a = el.attributes[i];
175
- if (a.name === "style") continue;
176
- at.push(a.name + '="' + (a.value.length > 150 ? a.value.substring(0,150) : a.value).replace(/"/g,"'") + '"');
177
- }
178
- var as = at.length ? " " + at.join(" ") : "";
179
- var dt = "";
180
- for (var j = 0; j < el.childNodes.length; j++) {
181
- if (el.childNodes[j].nodeType === 3) { var tx = (el.childNodes[j].textContent||"").trim(); if (tx) dt += tx.substring(0,100); }
182
- }
183
- var ch = "";
184
- for (var k = 0; k < el.children.length; k++) ch += ser(el.children[k], d+1, b);
185
- if (!as && !dt && !ch) return "";
186
- var r = "<"+t+as+">"+dt+ch+"</"+t+">";
187
- b.c -= r.length;
188
- return r;
189
- }
190
- return ser(document.body, 0, {c:500000});
191
- })()`);
163
+ const ariaSnap = await page.locator('body').ariaSnapshot();
192
164
 
193
165
  // AI kaldığı yerden devam etsin (her denemede effort artar)
194
166
  const newSteps = await healAndContinue(
@@ -196,7 +168,7 @@ export async function runCommand(
196
168
  stepIdx,
197
169
  steps,
198
170
  healScreenshot,
199
- domSnap as string,
171
+ ariaSnap,
200
172
  err.message,
201
173
  post.content || step.description,
202
174
  effort
@@ -352,31 +324,13 @@ async function executeMapStep(
352
324
  break;
353
325
 
354
326
  case "upload": {
355
- // Dosya yükleme - hidden file input'a dosya set et
356
327
  const filePath = rp(step.value || "{{IMAGE}}");
357
328
  if (!filePath || filePath.includes("{{")) break; // dosya yoksa atla
358
329
 
359
- let uploaded = false;
360
- for (const sel of selectors) {
361
- try {
362
- const input = await page.waitForSelector(sel, { timeout: 5000 });
363
- await input.setInputFiles(filePath);
364
- uploaded = true;
365
- break;
366
- } catch {}
367
- }
368
- if (!uploaded) {
369
- // Hidden input bulunamazsa, file chooser ile dene
370
- try {
371
- const [fileChooser] = await Promise.all([
372
- page.waitForEvent("filechooser", { timeout: 5000 }),
373
- page.click(selectors[0] || "[data-testid='fileInput']"),
374
- ]);
375
- await fileChooser.setFiles(filePath);
376
- uploaded = true;
377
- } catch {}
378
- }
379
- if (!uploaded) throw new Error(`Upload failed: ${filePath}`);
330
+ // Hidden input[type=file]'a direkt setInputFiles - butona tıklamadan
331
+ const fileInput = page.locator('input[type="file"]').first();
332
+ await fileInput.setInputFiles(filePath);
333
+
380
334
  if (step.waitMs) await page.waitForTimeout(step.waitMs);
381
335
  break;
382
336
  }
@@ -0,0 +1,70 @@
1
+ {
2
+ "post": {
3
+ "platform": "linkedin",
4
+ "action": "post",
5
+ "description": "LinkedIn üzerinden yeni bir metin postu paylaşma",
6
+ "version": 1,
7
+ "lastUpdated": "2026-03-14T12:00:00.000Z",
8
+ "parameters": [
9
+ "{{CONTENT}}"
10
+ ],
11
+ "steps": [
12
+ {
13
+ "action": "click",
14
+ "description": "Gönderi oluştur kutusuna tıkla",
15
+ "selector": "button.share-box-feed-entry__trigger",
16
+ "fallbackSelectors": [
17
+ "[aria-label*='Start a post']",
18
+ "[aria-label*='Gönderi başlat']",
19
+ "button[aria-label*='post']"
20
+ ]
21
+ },
22
+ {
23
+ "action": "wait",
24
+ "description": "Post oluşturma modalının açılmasını bekle",
25
+ "waitMs": 1500
26
+ },
27
+ {
28
+ "action": "click",
29
+ "description": "Metin alanına tıkla",
30
+ "selector": "[role='textbox'][aria-label*='Text editor']",
31
+ "fallbackSelectors": [
32
+ "[role='textbox'][contenteditable='true']",
33
+ ".ql-editor[contenteditable='true']",
34
+ "[aria-label*='Metin düzenleyici']"
35
+ ]
36
+ },
37
+ {
38
+ "action": "type",
39
+ "description": "Post metnini yaz",
40
+ "selector": "[role='textbox'][aria-label*='Text editor']",
41
+ "value": "{{CONTENT}}",
42
+ "fallbackSelectors": [
43
+ "[role='textbox'][contenteditable='true']",
44
+ ".ql-editor[contenteditable='true']"
45
+ ]
46
+ },
47
+ {
48
+ "action": "wait",
49
+ "description": "Metin girişi sonrası bekleme",
50
+ "waitMs": 1000
51
+ },
52
+ {
53
+ "action": "click",
54
+ "description": "Gönder butonuna tıkla",
55
+ "selector": "button.share-actions__primary-action",
56
+ "fallbackSelectors": [
57
+ "[aria-label='Post']",
58
+ "[aria-label='Gönder']",
59
+ "button[aria-label*='Post']",
60
+ "button[aria-label*='Paylaş']"
61
+ ]
62
+ },
63
+ {
64
+ "action": "wait",
65
+ "description": "Post gönderimini bekle",
66
+ "waitMs": 2000
67
+ }
68
+ ]
69
+ }
70
+ }
package/maps/x.json CHANGED
@@ -1,69 +1,68 @@
1
1
  {
2
- "platform": "x",
3
- "version": 1,
4
- "lastUpdated": "2026-03-14T11:09:04.533Z",
5
- "steps": [
6
- {
7
- "action": "click",
8
- "description": "Click the new post button in the sidebar",
9
- "selector": "[data-testid='SideNav_NewTweet_Button']",
10
- "fallbackSelectors": [
11
- "a[href='/compose/post']",
12
- "[aria-label*='Gönder'][role='link']",
13
- "nav [role='link']:last-child"
14
- ]
15
- },
16
- {
17
- "action": "wait",
18
- "description": "Wait for the compose dialog to appear",
19
- "waitMs": 1500,
20
- "selector": "[data-testid='tweetTextarea_0']",
21
- "fallbackSelectors": [
22
- "[role='dialog'] [contenteditable='true']",
23
- ".public-DraftEditor-content",
24
- "[data-testid='toolBar'] ~ div [contenteditable]"
25
- ]
26
- },
27
- {
28
- "action": "click",
29
- "description": "Focus the text input area",
30
- "selector": "[data-testid='tweetTextarea_0']",
31
- "fallbackSelectors": [
32
- "[role='dialog'] [contenteditable='true']",
33
- ".public-DraftEditor-content",
34
- "[data-testid='tweetTextarea_0RichTextInputContainer'] [contenteditable]"
35
- ]
36
- },
37
- {
38
- "action": "type",
39
- "description": "Type the post content",
40
- "selector": "[data-testid='tweetTextarea_0']",
41
- "value": "{{CONTENT}}",
42
- "fallbackSelectors": [
43
- "[role='dialog'] [contenteditable='true']",
44
- ".public-DraftEditor-content",
45
- "[data-testid='tweetTextarea_0RichTextInputContainer'] [contenteditable]"
46
- ]
47
- },
48
- {
49
- "action": "wait",
50
- "description": "Wait for content to be entered",
51
- "waitMs": 500
52
- },
53
- {
54
- "action": "click",
55
- "description": "Click the Post/Gönder button to submit",
56
- "selector": "[data-testid='tweetButton']",
57
- "fallbackSelectors": [
58
- "[data-testid='tweetButtonInline']",
59
- "[role='dialog'] [role='button'][data-testid='tweetButton']",
60
- "button[type='button'] span span:has-text('Gönder')"
61
- ]
62
- },
63
- {
64
- "action": "wait",
65
- "description": "Wait for post to be submitted",
66
- "waitMs": 2000
67
- }
68
- ]
2
+ "post": {
3
+ "platform": "x",
4
+ "action": "post",
5
+ "description": "X (Twitter) üzerinden yeni bir metin postu paylaşma",
6
+ "version": 1,
7
+ "lastUpdated": "2026-03-14T12:00:00.000Z",
8
+ "parameters": [
9
+ "{{CONTENT}}"
10
+ ],
11
+ "steps": [
12
+ {
13
+ "action": "click",
14
+ "description": "Sidebar'daki post butonuna tıkla",
15
+ "selector": "[data-testid='SideNav_NewTweet_Button']",
16
+ "fallbackSelectors": [
17
+ "a[href='/compose/post']",
18
+ "[aria-label*='Post'][role='link']",
19
+ "[aria-label*='Gönder'][role='link']",
20
+ "[data-testid='FloatingActionButton']"
21
+ ]
22
+ },
23
+ {
24
+ "action": "wait",
25
+ "description": "Compose dialog açılmasını bekle",
26
+ "waitMs": 1500
27
+ },
28
+ {
29
+ "action": "click",
30
+ "description": "Metin alanına tıkla",
31
+ "selector": "[data-testid='tweetTextarea_0']",
32
+ "fallbackSelectors": [
33
+ "[role='textbox'][contenteditable='true']",
34
+ ".public-DraftEditor-content"
35
+ ]
36
+ },
37
+ {
38
+ "action": "type",
39
+ "description": "Post metnini yaz",
40
+ "selector": "[data-testid='tweetTextarea_0']",
41
+ "value": "{{CONTENT}}",
42
+ "fallbackSelectors": [
43
+ "[role='textbox'][contenteditable='true']",
44
+ ".public-DraftEditor-content"
45
+ ]
46
+ },
47
+ {
48
+ "action": "wait",
49
+ "description": "Metin girişi sonrası bekleme",
50
+ "waitMs": 500
51
+ },
52
+ {
53
+ "action": "click",
54
+ "description": "Gönder butonuna tıkla",
55
+ "selector": "[data-testid='tweetButton']",
56
+ "fallbackSelectors": [
57
+ "[data-testid='tweetButtonInline']",
58
+ "[role='button'][data-testid*='tweet']"
59
+ ]
60
+ },
61
+ {
62
+ "action": "wait",
63
+ "description": "Post gönderimini bekle",
64
+ "waitMs": 2000
65
+ }
66
+ ]
67
+ }
69
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "social-agent-cli",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "AI-powered social media agent - free APIs + browser automation with self-healing selectors",
5
5
  "type": "module",
6
6
  "bin": {
@@ -162,48 +162,14 @@ export class BrowserDriver {
162
162
  const screenshotPath = join(PATHS.screenshots, `${this.platform}_learn_${actionName}.png`);
163
163
  await page.screenshot({ path: screenshotPath, fullPage: false });
164
164
 
165
- // DOM snapshot - evaluate'e string olarak geç (tsx __name sorunu önlenir)
166
- const dom = await page.evaluate(`(function() {
167
- var SKIP = {"script":1,"style":1,"noscript":1,"link":1,"meta":1};
168
- function ser(el, depth, budget) {
169
- if (depth > 15 || budget.c <= 0) return "";
170
- var tag = el.tagName.toLowerCase();
171
- if (SKIP[tag]) return "";
172
- if (tag === "svg") return "<svg/>";
173
- var st = window.getComputedStyle(el);
174
- if (st.display === "none" || st.visibility === "hidden") return "";
175
- var attrs = [];
176
- for (var i = 0; i < el.attributes.length; i++) {
177
- var a = el.attributes[i];
178
- if (a.name === "style") continue;
179
- var v = a.value.length > 150 ? a.value.substring(0,150) : a.value;
180
- attrs.push(a.name + '="' + v.replace(/"/g, "'") + '"');
181
- }
182
- var as = attrs.length ? " " + attrs.join(" ") : "";
183
- var dt = "";
184
- for (var j = 0; j < el.childNodes.length; j++) {
185
- if (el.childNodes[j].nodeType === 3) {
186
- var t = (el.childNodes[j].textContent || "").trim();
187
- if (t) dt += t.substring(0, 100);
188
- }
189
- }
190
- var ch = "";
191
- for (var k = 0; k < el.children.length; k++) {
192
- ch += ser(el.children[k], depth + 1, budget);
193
- }
194
- if (!as && !dt && !ch) return "";
195
- var r = "<" + tag + as + ">" + dt + ch + "</" + tag + ">";
196
- budget.c -= r.length;
197
- return r;
198
- }
199
- return ser(document.body, 0, {c: 500000});
200
- })()`);
165
+ // ARIA Snapshot - accessibility tree (98x daha küçük, sadece interaktif elementler)
166
+ const ariaSnapshot = await page.locator('body').ariaSnapshot();
201
167
 
202
168
  console.log(`[${this.platform}] Screenshot: ${screenshotPath}`);
203
- console.log(`[${this.platform}] DOM: ${dom.length} karakter`);
169
+ console.log(`[${this.platform}] Accessibility tree: ${ariaSnapshot.length} bytes`);
204
170
  console.log(`[${this.platform}] Claude analiz ediyor...`);
205
171
 
206
- const map = await generateMap(this.platform, screenshotPath, dom, taskDescription, actionName);
172
+ const map = await generateMap(this.platform, screenshotPath, ariaSnapshot, taskDescription, actionName);
207
173
 
208
174
  console.log(`[${this.platform}] "${actionName}" map oluşturuldu (${map.steps.length} adım)`);
209
175
  await this.close();
@@ -1,66 +0,0 @@
1
- {
2
- "platform": "linkedin",
3
- "version": 1,
4
- "action": "linkedin_post",
5
- "description": "LinkedIn platformunda metin içerikli post paylaşma",
6
- "lastUpdated": "2026-03-14T11:33:00.454Z",
7
- "parameters": [
8
- "{{CONTENT}}"
9
- ],
10
- "steps": [
11
- {
12
- "action": "goto",
13
- "description": "LinkedIn ana sayfasına git",
14
- "url": "https://www.linkedin.com/feed/"
15
- },
16
- {
17
- "action": "wait",
18
- "description": "Sayfanın yüklenmesini bekle",
19
- "waitMs": 3000
20
- },
21
- {
22
- "action": "click",
23
- "description": "Gönderi oluşturma kutusuna tıkla",
24
- "selector": "button.rBknNMyPiauEsphJukuFITVzZCRzAenPRs",
25
- "fallbackSelectors": [
26
- ".share-box-feed-entry__top-bar button.artdeco-button--tertiary",
27
- "button[class*='share-box-feed-entry']"
28
- ]
29
- },
30
- {
31
- "action": "wait",
32
- "description": "Post oluşturma modalının açılmasını bekle",
33
- "waitMs": 2000
34
- },
35
- {
36
- "action": "type",
37
- "description": "Post içeriğini yaz",
38
- "selector": "div.ql-editor[role='textbox']",
39
- "fallbackSelectors": [
40
- "div[aria-label='Metin Düzenleyici']",
41
- "div.editor-content div[contenteditable='true']",
42
- "div[data-placeholder='Ne hakkında konuşmak istiyorsunuz?']"
43
- ],
44
- "value": "{{CONTENT}}"
45
- },
46
- {
47
- "action": "wait",
48
- "description": "İçeriğin yazılmasını bekle",
49
- "waitMs": 1000
50
- },
51
- {
52
- "action": "click",
53
- "description": "Gönder butonuna tıkla",
54
- "selector": "button.share-actions__primary-action",
55
- "fallbackSelectors": [
56
- "button[aria-label='Gönderi paylaş']",
57
- "button.artdeco-button--primary[class*='share-actions']"
58
- ]
59
- },
60
- {
61
- "action": "wait",
62
- "description": "Gönderinin paylaşılmasını bekle",
63
- "waitMs": 3000
64
- }
65
- ]
66
- }
@@ -1,37 +0,0 @@
1
- {
2
- "platform": "linkedin",
3
- "version": 1,
4
- "action": "linkedin_send_connection_request",
5
- "description": "LinkedIn mynetwork/grow sayfasından bir kişiye bağlantı isteği gönderme",
6
- "lastUpdated": "2026-03-14T11:44:38.451Z",
7
- "parameters": [
8
- "{{USERNAME}}"
9
- ],
10
- "steps": [
11
- {
12
- "action": "goto",
13
- "description": "LinkedIn Ağım/Grow sayfasına git",
14
- "url": "https://www.linkedin.com/mynetwork/grow/"
15
- },
16
- {
17
- "action": "wait",
18
- "description": "Sayfa ve öneri kartlarının yüklenmesini bekle",
19
- "waitMs": 3000
20
- },
21
- {
22
- "action": "click",
23
- "description": "{{USERNAME}} kişisinin bağlantı kurma (Connect) butonuna tıkla",
24
- "selector": "button[aria-label*='{{USERNAME}}'][aria-label*='bağlantı' i]",
25
- "fallbackSelectors": [
26
- "button[aria-label*='{{USERNAME}}'][aria-label*='Connect' i]",
27
- "button[aria-label*='{{USERNAME}}'][aria-label*='Invite' i]",
28
- "//button[contains(@aria-label,'{{USERNAME}}') and (contains(@aria-label,'bağlantı') or contains(@aria-label,'Connect'))]"
29
- ]
30
- },
31
- {
32
- "action": "wait",
33
- "description": "Bağlantı isteği gönderilmesini veya modal açılmasını bekle",
34
- "waitMs": 2000
35
- }
36
- ]
37
- }
@@ -1,66 +0,0 @@
1
- {
2
- "platform": "linkedin",
3
- "action": "post",
4
- "description": "LinkedIn üzerinden yeni bir metin postu paylaşma",
5
- "version": 1,
6
- "lastUpdated": "2026-03-14T12:00:00.000Z",
7
- "parameters": ["{{CONTENT}}"],
8
- "steps": [
9
- {
10
- "action": "click",
11
- "description": "Gönderi oluştur kutusuna tıkla",
12
- "selector": "button.share-box-feed-entry__trigger",
13
- "fallbackSelectors": [
14
- "[aria-label*='Start a post']",
15
- "[aria-label*='Gönderi başlat']",
16
- "button[aria-label*='post']"
17
- ]
18
- },
19
- {
20
- "action": "wait",
21
- "description": "Post oluşturma modalının açılmasını bekle",
22
- "waitMs": 1500
23
- },
24
- {
25
- "action": "click",
26
- "description": "Metin alanına tıkla",
27
- "selector": "[role='textbox'][aria-label*='Text editor']",
28
- "fallbackSelectors": [
29
- "[role='textbox'][contenteditable='true']",
30
- ".ql-editor[contenteditable='true']",
31
- "[aria-label*='Metin düzenleyici']"
32
- ]
33
- },
34
- {
35
- "action": "type",
36
- "description": "Post metnini yaz",
37
- "selector": "[role='textbox'][aria-label*='Text editor']",
38
- "value": "{{CONTENT}}",
39
- "fallbackSelectors": [
40
- "[role='textbox'][contenteditable='true']",
41
- ".ql-editor[contenteditable='true']"
42
- ]
43
- },
44
- {
45
- "action": "wait",
46
- "description": "Metin girişi sonrası bekleme",
47
- "waitMs": 1000
48
- },
49
- {
50
- "action": "click",
51
- "description": "Gönder butonuna tıkla",
52
- "selector": "button.share-actions__primary-action",
53
- "fallbackSelectors": [
54
- "[aria-label='Post']",
55
- "[aria-label='Gönder']",
56
- "button[aria-label*='Post']",
57
- "button[aria-label*='Paylaş']"
58
- ]
59
- },
60
- {
61
- "action": "wait",
62
- "description": "Post gönderimini bekle",
63
- "waitMs": 2000
64
- }
65
- ]
66
- }
package/maps/x_post.json DELETED
@@ -1,64 +0,0 @@
1
- {
2
- "platform": "x",
3
- "action": "post",
4
- "description": "X (Twitter) üzerinden yeni bir metin postu paylaşma",
5
- "version": 1,
6
- "lastUpdated": "2026-03-14T12:00:00.000Z",
7
- "parameters": ["{{CONTENT}}"],
8
- "steps": [
9
- {
10
- "action": "click",
11
- "description": "Sidebar'daki post butonuna tıkla",
12
- "selector": "[data-testid='SideNav_NewTweet_Button']",
13
- "fallbackSelectors": [
14
- "a[href='/compose/post']",
15
- "[aria-label*='Post'][role='link']",
16
- "[aria-label*='Gönder'][role='link']",
17
- "[data-testid='FloatingActionButton']"
18
- ]
19
- },
20
- {
21
- "action": "wait",
22
- "description": "Compose dialog açılmasını bekle",
23
- "waitMs": 1500
24
- },
25
- {
26
- "action": "click",
27
- "description": "Metin alanına tıkla",
28
- "selector": "[data-testid='tweetTextarea_0']",
29
- "fallbackSelectors": [
30
- "[role='textbox'][contenteditable='true']",
31
- ".public-DraftEditor-content"
32
- ]
33
- },
34
- {
35
- "action": "type",
36
- "description": "Post metnini yaz",
37
- "selector": "[data-testid='tweetTextarea_0']",
38
- "value": "{{CONTENT}}",
39
- "fallbackSelectors": [
40
- "[role='textbox'][contenteditable='true']",
41
- ".public-DraftEditor-content"
42
- ]
43
- },
44
- {
45
- "action": "wait",
46
- "description": "Metin girişi sonrası bekleme",
47
- "waitMs": 500
48
- },
49
- {
50
- "action": "click",
51
- "description": "Gönder butonuna tıkla",
52
- "selector": "[data-testid='tweetButton']",
53
- "fallbackSelectors": [
54
- "[data-testid='tweetButtonInline']",
55
- "[role='button'][data-testid*='tweet']"
56
- ]
57
- },
58
- {
59
- "action": "wait",
60
- "description": "Post gönderimini bekle",
61
- "waitMs": 2000
62
- }
63
- ]
64
- }
@@ -1,39 +0,0 @@
1
- {
2
- "platform": "x",
3
- "version": 1,
4
- "lastUpdated": "2026-03-14T11:21:59.765Z",
5
- "action": "tweet_beğenmeyi_öğren",
6
- "description": "tweet beğenmeyi öğren",
7
- "parameters": [
8
- "{{TWEET_URL}}"
9
- ],
10
- "steps": [
11
- {
12
- "action": "goto",
13
- "description": "Beğenilecek tweet'in sayfasına git",
14
- "url": "{{TWEET_URL}}",
15
- "fallbackSelectors": []
16
- },
17
- {
18
- "action": "wait",
19
- "description": "Tweet içeriğinin yüklenmesini bekle",
20
- "waitMs": 2000,
21
- "fallbackSelectors": []
22
- },
23
- {
24
- "action": "click",
25
- "description": "Tweet'in altındaki beğen (kalp) butonuna tıkla",
26
- "selector": "[data-testid=\"like\"]",
27
- "fallbackSelectors": [
28
- "[aria-label*=\"Beğen\"]",
29
- "button[role=\"button\"] svg[viewBox=\"0 0 24 24\"]"
30
- ]
31
- },
32
- {
33
- "action": "wait",
34
- "description": "Beğeni animasyonunun tamamlanmasını bekle",
35
- "waitMs": 1000,
36
- "fallbackSelectors": []
37
- }
38
- ]
39
- }
@@ -1,46 +0,0 @@
1
- {
2
- "platform": "x",
3
- "version": 1,
4
- "lastUpdated": "2026-03-14T12:01:17.966Z",
5
- "action": "tweet_retweet_öğren",
6
- "description": "Belirtilen tweet sayfasına git, retweet butonuna tıkla ve onay ver",
7
- "parameters": [
8
- "{{TWEET_URL}}"
9
- ],
10
- "steps": [
11
- {
12
- "action": "goto",
13
- "description": "Belirtilen tweet sayfasına git",
14
- "url": "{{TWEET_URL}}"
15
- },
16
- {
17
- "action": "wait",
18
- "description": "Tweet sayfasının yüklenmesini bekle",
19
- "waitMs": 2000
20
- },
21
- {
22
- "action": "click",
23
- "description": "Retweet butonuna tıkla",
24
- "selector": "[data-testid='retweet']",
25
- "fallbackSelectors": [
26
- "button[aria-label*='Yeniden gönder']",
27
- "button[aria-label*='Repost']",
28
- "[data-testid='unretweet']"
29
- ]
30
- },
31
- {
32
- "action": "wait",
33
- "description": "Retweet menüsünün açılmasını bekle",
34
- "waitMs": 1000
35
- },
36
- {
37
- "action": "click",
38
- "description": "Yeniden gönder seçeneğine tıkla",
39
- "selector": "[data-testid='retweetConfirm']",
40
- "fallbackSelectors": [
41
- "[role='menuitem'][data-testid='retweetConfirm']",
42
- "[role='menu'] [role='menuitem']:first-child"
43
- ]
44
- }
45
- ]
46
- }