social-agent-cli 1.5.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 ──────────────────────────────────────────────
@@ -215,6 +235,7 @@ ${domSnapshot}
215
235
  Kurallar:
216
236
  - ARIA snapshot'taki role ve name bilgilerini kullanarak selector oluştur
217
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.
218
239
  - Her adım için 2+ fallback selector
219
240
  - Parametreler için placeholder kullan: {{CONTENT}} = metin, {{USERNAME}} = kullanıcı adı, {{URL}} = link, vs.
220
241
  - Hangi parametreleri kullandığını "parameters" alanında listele
@@ -261,7 +282,7 @@ const REMAINING_STEPS_SCHEMA = JSON.stringify({
261
282
  items: {
262
283
  type: "object",
263
284
  properties: {
264
- action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto"] },
285
+ action: { type: "string", enum: ["click", "type", "wait", "keypress", "goto", "upload"] },
265
286
  description: { type: "string" },
266
287
  selector: { type: "string" },
267
288
  value: { type: "string" },
@@ -320,7 +341,8 @@ GÖREV: Ekranın mevcut durumunu analiz et. Amaca ulaşmak için kalan adımlar
320
341
  - Belki ekstra bir adım gerekiyor (popup kapatma, scroll, vb.)
321
342
  - Belki farklı bir yol izlenmeli
322
343
  - {{CONTENT}} = post metni placeholder'ı
323
- - 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}}`;
324
346
 
325
347
  console.log(`\n[heal] AI ekranı analiz ediyor (effort: ${effort})...\n`);
326
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
@@ -324,31 +324,13 @@ async function executeMapStep(
324
324
  break;
325
325
 
326
326
  case "upload": {
327
- // Dosya yükleme - hidden file input'a dosya set et
328
327
  const filePath = rp(step.value || "{{IMAGE}}");
329
328
  if (!filePath || filePath.includes("{{")) break; // dosya yoksa atla
330
329
 
331
- let uploaded = false;
332
- for (const sel of selectors) {
333
- try {
334
- const input = await page.waitForSelector(sel, { timeout: 5000 });
335
- await input.setInputFiles(filePath);
336
- uploaded = true;
337
- break;
338
- } catch {}
339
- }
340
- if (!uploaded) {
341
- // Hidden input bulunamazsa, file chooser ile dene
342
- try {
343
- const [fileChooser] = await Promise.all([
344
- page.waitForEvent("filechooser", { timeout: 5000 }),
345
- page.click(selectors[0] || "[data-testid='fileInput']"),
346
- ]);
347
- await fileChooser.setFiles(filePath);
348
- uploaded = true;
349
- } catch {}
350
- }
351
- 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
+
352
334
  if (step.waitMs) await page.waitForTimeout(step.waitMs);
353
335
  break;
354
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.5.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": {
@@ -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
- }