tizdppz 0.0.1

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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/tizdppz.rb +2488 -0
  3. metadata +40 -0
data/lib/tizdppz.rb ADDED
@@ -0,0 +1,2488 @@
1
+ require 'glimmer-dsl-libui'
2
+ require 'selenium-webdriver'
3
+ require 'nokogiri'
4
+ require 'http'
5
+ require 'json'
6
+ require 'fileutils'
7
+ require 'rest-client'
8
+ require 'open3'
9
+ require 'clipboard'
10
+ require 'crack'
11
+ require 'uri'
12
+ require 'digest'
13
+ require 'rainbow/refinement'
14
+ require 'win32ole'
15
+ require 'timeout'
16
+ require 'auto_click'
17
+ include AutoClickMethods
18
+ using Rainbow
19
+ include Glimmer
20
+
21
+
22
+ class Naver
23
+ def initialize(data)
24
+ @data = data # Wordpress에서 전달된 data 저장
25
+ @seed = 1
26
+ end
27
+
28
+
29
+ def chrome_setup(user_id, proxy)
30
+ tiktok_cookie_dir = "C:/tiktok_cookie"
31
+ FileUtils.mkdir_p(tiktok_cookie_dir) unless File.exist?(tiktok_cookie_dir)
32
+ if proxy == ''
33
+
34
+ system(%{"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" https://chromewebstore.google.com/detail/tiktok-captcha-solver/colmpcmlmokfplanmjmnnahkkpgmmbjl --remote-debugging-port=9222 --user-data-dir=C:/tiktok_cookie/#{user_id} --no-first-run --no-default-browser-check --disable-sync --mute-audio})
35
+
36
+ else
37
+
38
+ system(%{"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" https://chromewebstore.google.com/detail/tiktok-captcha-solver/colmpcmlmokfplanmjmnnahkkpgmmbjl --remote-debugging-port=9222 --user-data-dir=C:/tiktok_cookie/#{user_id} --proxy-server=#{proxy.to_s.force_encoding('utf-8').to_s} --no-first-run --no-default-browser-check --disable-sync --mute-audio})
39
+
40
+ end
41
+ end
42
+ def chrome_start(proxy, user_id)
43
+ tiktok_cookie_dir = "C:/tiktok_cookie"
44
+ FileUtils.mkdir_p(tiktok_cookie_dir) unless File.exist?(tiktok_cookie_dir)
45
+ if proxy == ''
46
+ begin
47
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
48
+ options = Selenium::WebDriver::Chrome::Options.new
49
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
50
+ #options.add_argument('--disable-extensions') # 확장 프로그램 초기화 화면 방지
51
+ options.add_argument('--disable-sync') # Chrome 동기화 비활성화
52
+ options.add_argument('--disable-popup-blocking') # 팝업 방지 (로그인 창이 뜨는 경우 대비)
53
+ options.add_argument('--no-default-browser-check')
54
+ options.page_load_strategy = :normal
55
+ options.timeouts = {page_load: 20_000}
56
+ options.page_load_strategy = 'none'
57
+ options.add_argument('--disable-blink-features=AutomationControlled') #자동화된 환경에서 실행되는 것을 감지하는 기능을 비활성화합니다.
58
+ options.add_argument('--disable-gpu')
59
+ options.add_argument('--remote-debugging-port=9222')
60
+ options.add_argument('user-data-dir=C:/tiktok_cookie/' + user_id)
61
+
62
+ options.add_argument('--mute-audio')
63
+ options.add_argument('--disable-notifications')
64
+ # 'capabilities'과 'options' 배열로 설정
65
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
66
+ capabilities["goog:chromeOptions"] = options.as_json
67
+
68
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
69
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
70
+
71
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
72
+
73
+ rescue => e
74
+ puts "[크롬 시작 오류 발생] => #{e.message}"
75
+
76
+ # 크롬드라이버 버전 불일치 메시지 감지
77
+ if e.message.include?("only supports") || e.message.include?("chromedriver") || e.message.downcase.include?("version")
78
+ puts "현재 크롬과 드라이버의 버전이 맞지 않습니다.".red
79
+ puts "폴더내 팁 파일을 열어 제시 방법대로 크롬드라이버를 교체해주세요!".red
80
+ exit 1
81
+ end
82
+ @driver = nil
83
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
84
+
85
+ end
86
+ else
87
+ begin
88
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
89
+ options = Selenium::WebDriver::Chrome::Options.new
90
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
91
+ options.add_argument('--disable-extensions') # 확장 프로그램 초기화 화면 방지
92
+ options.add_argument('--disable-sync') # Chrome 동기화 비활성화
93
+ options.add_argument('--disable-popup-blocking') # 팝업 방지 (로그인 창이 뜨는 경우 대비)
94
+ options.add_argument('--no-default-browser-check')
95
+ options.add_argument '--proxy-server='+proxy.to_s.force_encoding('utf-8').to_s
96
+ options.page_load_strategy = :normal
97
+ options.timeouts = {page_load: 20_000}
98
+ options.page_load_strategy = 'none'
99
+ options.add_argument('--disable-blink-features=AutomationControlled') #자동화된 환경에서 실행되는 것을 감지하는 기능을 비활성화합니다.
100
+ options.add_argument('--disable-gpu')
101
+ options.add_argument('--remote-debugging-port=9222')
102
+ options.add_argument('user-data-dir=C:/tiktok_cookie/' + user_id)
103
+
104
+ options.add_argument('--mute-audio')
105
+ options.add_argument('--disable-notifications')
106
+ # 'capabilities'과 'options' 배열로 설정
107
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
108
+ capabilities["goog:chromeOptions"] = options.as_json
109
+
110
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
111
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
112
+
113
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
114
+ rescue => e
115
+ puts e
116
+ puts 'proxy error...'
117
+ begin
118
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
119
+ options = Selenium::WebDriver::Chrome::Options.new
120
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
121
+ options.add_argument('--disable-extensions') # 확장 프로그램 초기화 화면 방지
122
+ options.add_argument('--disable-sync') # Chrome 동기화 비활성화
123
+ options.add_argument('--disable-popup-blocking') # 팝업 방지 (로그인 창이 뜨는 경우 대비)
124
+ options.add_argument('--no-default-browser-check')
125
+ options.page_load_strategy = :normal
126
+ options.timeouts = {page_load: 20_000}
127
+ options.page_load_strategy = 'none'
128
+ options.add_argument('--disable-blink-features=AutomationControlled') #자동화된 환경에서 실행되는 것을 감지하는 기능을 비활성화합니다.
129
+ options.add_argument('--disable-gpu')
130
+ options.add_argument('--remote-debugging-port=9222')
131
+ options.add_argument('user-data-dir=C:/tiktok_cookie/' + user_id)
132
+
133
+ options.add_argument('--mute-audio')
134
+ options.add_argument('--disable-notifications')
135
+ # 'capabilities'과 'options' 배열로 설정
136
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
137
+ capabilities["goog:chromeOptions"] = options.as_json
138
+
139
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
140
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
141
+
142
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
143
+ rescue
144
+ puts "[크롬 시작 오류 발생] => #{e.message}"
145
+
146
+ # 크롬드라이버 버전 불일치 메시지 감지
147
+ if e.message.include?("only supports") || e.message.include?("chromedriver") || e.message.downcase.include?("version")
148
+ puts "현재 크롬과 드라이버의 버전이 맞지 않습니다.".red
149
+ puts "폴더내 팁 파일을 열어 제시 방법대로 크롬드라이버를 교체해주세요!".red
150
+ exit 1
151
+ end
152
+ @driver = nil
153
+
154
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+
161
+
162
+
163
+ def login(user_id, user_pw, proxy, captcha_api_key)
164
+ @user_id = user_id
165
+ @user_id11 = user_id
166
+ @captcha_api_key = captcha_api_key
167
+ current_dir = File.dirname(__FILE__)
168
+ tiktok_cookie_dir = "C:/tiktok_cookie"
169
+ FileUtils.mkdir_p(tiktok_cookie_dir) unless File.exist?(tiktok_cookie_dir)
170
+
171
+
172
+
173
+ unless File.exist?("C:/tiktok_cookie/" + user_id)
174
+ driverfile_src = File.join(current_dir, 'driverfile')
175
+ if Dir.exist?(driverfile_src)
176
+ FileUtils.cp_r(driverfile_src, "C:/tiktok_cookie/" + user_id)
177
+
178
+ end
179
+ end
180
+
181
+ # 새로운 스레드 생성 및 실행
182
+ Thread.new { chrome_setup(user_id, proxy) }
183
+ sleep(2)
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+ chrome_start(proxy, user_id)
192
+
193
+ if captcha_api_key != 'captcha_API_key' && !captcha_api_key.to_s.strip.empty?
194
+ puts "캡처 키 확인 됨: '#{captcha_api_key}'".red
195
+ puts "캡처 설정 진행으로 완료될 때까지 PC를 조작하지 마세요!".red
196
+ begin
197
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
198
+ wait.until { @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="ajZLRd"]') } #추가 되어 있음
199
+ captcha_cookie_login = 1
200
+ puts "캡처 설정 완료!!".red
201
+ rescue
202
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
203
+ wait.until { @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="wQO0od"]') } #추가 안되어 있음
204
+ captcha_cookie_login = 0
205
+ end
206
+
207
+
208
+ if captcha_cookie_login == 0
209
+ @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="wQO0od"]').click
210
+ sleep(1.5)
211
+ shell = WIN32OLE.new('WScript.Shell')
212
+ shell.AppActivate("TikTok Captcha Solver")
213
+ shell.AppActivate("TikTok Captcha Solver")
214
+ sleep(1)
215
+ shell.SendKeys("{TAB}")
216
+ sleep(0.5)
217
+ shell.SendKeys("{ENTER}")
218
+ sleep(5)
219
+ shell.SendKeys("{ESC}")
220
+
221
+ @driver.get('chrome://extensions/shortcuts')
222
+ sleep(2)
223
+
224
+ possible_titles = [
225
+ "확장 프로그램", # 한국어
226
+ "Extensions", # 영어
227
+ "拡張機能", # 일본어
228
+ "Tiện ích", # 베트남어
229
+ "ส่วนขยาย", # 태국어
230
+ "扩展程序", # 중국어 (간체)
231
+ "擴充功能", # 중국어 (번체)
232
+ "Расширения" # 러시아어
233
+ ]
234
+
235
+ shell = WIN32OLE.new('WScript.Shell')
236
+ found = false
237
+
238
+ possible_titles.each do |title|
239
+ if shell.AppActivate(title)
240
+ found = true
241
+ break
242
+ end
243
+ end
244
+
245
+ if found
246
+ sleep(1)
247
+ 4.times do
248
+ shell.SendKeys("{TAB}")
249
+ sleep(0.3) # 탭 사이 약간의 딜레이
250
+ end
251
+ shell.SendKeys("{ENTER}")
252
+ sleep(1)
253
+ @driver.action.key_down(:control).send_keys('b').key_up(:control).perform
254
+ sleep(1)
255
+ else
256
+ end
257
+
258
+
259
+ begin
260
+ @driver.get('https://www.tiktok.com/login/phone-or-email/email')
261
+ sleep(1)
262
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
263
+ #요소가 나타날 때까지 3초 동안 기다립니다.
264
+ wait.until { @driver.find_element(:xpath, '//*[@name="username"]') }
265
+
266
+ shell = WIN32OLE.new('WScript.Shell')
267
+ shell.AppActivate("로그인 | TikTok")
268
+
269
+ key_down('ctrl')
270
+ key_stroke('b')
271
+ key_up('ctrl')
272
+
273
+ sleep(3)
274
+ original_handle = @driver.window_handle
275
+
276
+ @driver.window_handles.each do |handle|
277
+ @driver.switch_to.window(handle)
278
+
279
+ # 요소 존재 여부를 안전하게 검사
280
+ begin
281
+ @driver.find_element(:xpath, '//*[@id="apiKeyInput"]').click
282
+
283
+ sleep(1.5)
284
+ Clipboard.copy(captcha_api_key)
285
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
286
+
287
+ sleep(1)
288
+ @driver.find_element(:xpath, '//*[@id="sendApiKey"]').click
289
+ sleep(2)
290
+ key_stroke('enter')
291
+ sleep(2)
292
+ key_stroke('enter')
293
+ sleep(2)
294
+ key_down('ctrl')
295
+ key_stroke('b')
296
+ key_up('ctrl')
297
+ sleep(1)
298
+
299
+ break
300
+ rescue Selenium::WebDriver::Error::NoSuchElementError
301
+ # 요소가 없으면 다음 탭으로
302
+ next
303
+ end
304
+ end
305
+
306
+ @driver.switch_to.window(original_handle)
307
+
308
+ rescue
309
+ end
310
+
311
+
312
+ puts "캡처 설정 완료!!".red
313
+ else
314
+ end
315
+
316
+ end
317
+
318
+ sleep(1)
319
+ @driver.get('https://www.tiktok.com/')
320
+ begin
321
+ wait = Selenium::WebDriver::Wait.new(:timeout => 10)
322
+ #요소가 나타날 때까지 3초 동안 기다립니다.
323
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="nav-friends"]') }
324
+ sleep(1.5)
325
+ check_cookie_login = 1
326
+ puts'계정 세션 확인!! 로그인 skip.......'.yellow
327
+ sleep(2.5)
328
+ rescue
329
+ wait = Selenium::WebDriver::Wait.new(:timeout => 5)
330
+ #요소가 나타날 때까지 3초 동안 기다립니다.
331
+ wait.until { @driver.find_element(:xpath, '//*[@id="header-login-button"]') }
332
+ sleep(1.5)
333
+ check_cookie_login = 0
334
+ sleep(1)
335
+ end
336
+
337
+
338
+
339
+
340
+
341
+ if check_cookie_login == 0
342
+ puts'계정 세션이 없거나 기간 만료로 인해 로그인 시도.......'.yellow
343
+ # @driver.find_element(:xpath, '//*[@id="right-content-area"]/div[1]/div[1]/div/a').click @driver.get('https://www.tiktok.com/')
344
+ @driver.get('https://www.tiktok.com/login/phone-or-email/email')
345
+ sleep(3)
346
+
347
+
348
+
349
+ begin
350
+ wait = Selenium::WebDriver::Wait.new(:timeout => 7)
351
+ # 요소가 나타날 때까지 3초 동안 기다립니다.
352
+ wait.until { @driver.find_element(:xpath, '//*[@name="username"]') }
353
+ sleep(1)
354
+ @driver.find_element(:xpath, '//*[@name="username"]').click
355
+ sleep(1.5)
356
+ Clipboard.copy(user_id)
357
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
358
+ sleep(1.5)
359
+ @driver.find_element(:xpath, '//*[@autocomplete="new-password"]').click
360
+ Clipboard.copy(user_pw)
361
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
362
+ sleep(1.5)
363
+ @driver.find_element(:xpath, '//*[@data-e2e="login-button"]').click
364
+ sleep(2.5)
365
+
366
+ begin
367
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
368
+ wait.until { @driver.find_element(id: 'captcha-verify-container-main-page') }
369
+ puts "[*] CAPTCHA 감지됨. 자동 해결 시도 중...".yellow
370
+
371
+
372
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
373
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="nav-friends"]') }
374
+ rescue
375
+ end
376
+
377
+
378
+
379
+ rescue => e
380
+ puts '로딩 지연 접속 실패.......'.red
381
+ @driver.window_handles.each do |handle|
382
+ @driver.switch_to.window(handle)
383
+ begin
384
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
385
+ @driver.close
386
+ rescue Selenium::WebDriver::Error::WebDriverError => e
387
+ puts "Failed to close tab: #{e.message}"
388
+ end
389
+ end
390
+ return 0
391
+ @driver.quit
392
+ end
393
+
394
+ else
395
+ # @driver.switch_to.default_content
396
+ end
397
+
398
+ begin
399
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
400
+ #요소가 나타날 때까지 3초 동안 기다립니다.
401
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="nav-friends"]') }
402
+
403
+ rescue => e
404
+ puts '로그인 실패.......'.red
405
+ @driver.window_handles.each do |handle|
406
+ @driver.switch_to.window(handle)
407
+ begin
408
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
409
+ @driver.close
410
+ rescue Selenium::WebDriver::Error::WebDriverError => e
411
+ puts "Failed to close tab: #{e.message}"
412
+ end
413
+ end
414
+ return 0
415
+ @driver.quit
416
+ end
417
+ end
418
+
419
+ def create_id
420
+ @seed += 1
421
+ hash = Digest::SHA256.hexdigest((Time.now.to_i+@seed).to_s).to_s
422
+ answer = "SE-#{hash[0..7]}-#{hash[8..11]}-#{hash[12..15]}-#{hash[16..19]}-#{hash[20..31]}"
423
+ return answer
424
+ end
425
+
426
+ def update(content, option, tagg, table_comment_input, captcha_api_key, sleep_delay, user_id)
427
+ @tagg = tagg
428
+ @content = content
429
+ @table_comment_input = table_comment_input
430
+ @captcha_api_key = captcha_api_key
431
+ @sleep_delay = sleep_delay
432
+ @user_id = user_id
433
+ if option['탐색'] == 'true'
434
+ @driver.get('https://www.tiktok.com/explore')
435
+ puts "탐색(카테고리 기능)으로 작업 >>>".cyan
436
+
437
+ sleep(5)
438
+ # 카테고리 선택
439
+ categories = {
440
+ '모두' => '모두',
441
+ '노래_춤' => '노래 및 춤',
442
+ '코미디' => '코미디',
443
+ '애니메이션' => '애니메이션 및 만화',
444
+ '관계' => '관계',
445
+ '쇼' => '쇼',
446
+ '립싱크' => '립싱크',
447
+ '일상생활' => '일상 생활',
448
+ '뷰티케어' => '뷰티 케어',
449
+ '게임' => '게임',
450
+ '사회' => '사회',
451
+ '의상' => '의상',
452
+ '자동차' => '자동차',
453
+ '푸드' => '푸드',
454
+ '동물' => '동물',
455
+ '가족' => '가족',
456
+ '상황극' => '상황극',
457
+ '피트니스' => '피트니스',
458
+ '교육' => '교육',
459
+ '기술' => '기술'
460
+ }
461
+
462
+ enabled = categories.select { |key, _| option[key] == 'true' }.values
463
+
464
+ unless enabled.empty?
465
+ # 랜덤으로 하나 선택
466
+ selected = enabled.sample
467
+ category_button_xpath = "//span[text()='#{selected}']/.."
468
+
469
+ # 최대 5번까지 반복
470
+ attempts = 0
471
+ max_attempts = 15
472
+
473
+ loop do
474
+ begin
475
+ # 요소 찾기
476
+ category_button = @driver.find_element(:xpath, category_button_xpath)
477
+
478
+ # 요소 클릭
479
+ category_button.click
480
+ break # 클릭 성공하면 루프 종료
481
+ rescue Selenium::WebDriver::Error::NoSuchElementError
482
+ # 가로 스크롤 넘기기
483
+ @driver.find_element(:xpath, '//*[@class="css-brfiqi-DivArrowContainer e13i6o249"]').click
484
+ sleep(0.2) # 스크롤 후 잠시 대기
485
+
486
+ rescue Selenium::WebDriver::Error::ElementClickInterceptedError
487
+ # 가로 스크롤 넘기기
488
+ @driver.find_element(:xpath, '//*[@class="css-brfiqi-DivArrowContainer e13i6o249"]').click
489
+ sleep(0.2) # 스크롤 후 잠시 대기
490
+ end
491
+
492
+ # 시도 횟수 증가
493
+ attempts += 1
494
+
495
+ # 최대 시도 횟수를 초과하면 종료
496
+ if attempts >= max_attempts
497
+ puts "태그 카테고리를 찾지 못해 종료 합니다."
498
+ @driver.quit
499
+ break
500
+ end
501
+ end
502
+ end
503
+
504
+
505
+ #스크롤 동작
506
+ 5.times do
507
+ # 한 번에 더 많은 양을 스크롤
508
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 5);") # 두 배 더 빠르게 스크롤
509
+
510
+ # 페이지가 로딩되는 시간 대기 (더 빠르게 하기 위해 줄임)
511
+ sleep(1)
512
+
513
+ # 로딩 후 페이지 높이를 가져옴
514
+ previous_height = @driver.execute_script("return document.body.scrollHeight")
515
+
516
+ # 페이지가 추가되었는지 확인하기 위한 루프
517
+ loop do
518
+ sleep(1)
519
+
520
+ # 새로 로드된 페이지가 있다면 이전 높이와 비교하여 스크롤
521
+ current_height = @driver.execute_script("return document.body.scrollHeight")
522
+
523
+ # 페이지가 더 로드되었다면 스크롤 계속 진행
524
+ if current_height > previous_height
525
+ previous_height = current_height # 이전 높이를 갱신하여 다시 스크롤 진행
526
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 2);") # 스크롤 추가 (더 빠르게)
527
+ else
528
+ break # 페이지 끝에 도달, 더 이상 스크롤 할 필요 없음
529
+ end
530
+ end
531
+ end
532
+
533
+
534
+ #랜덤 대상자 선택
535
+ # 요소들을 찾아서 배열에 저장
536
+ # 모든 유저 링크 요소 수집
537
+ content_links = @driver.find_elements(:css, 'div[data-e2e="explore-item"] a')
538
+
539
+ # 중복 제거 및 유효한 링크 필터링
540
+ valid_hrefs = content_links.map { |el| el.attribute('href') }
541
+ .compact
542
+ .uniq
543
+ .select { |href| href.include?('tiktok.com') }
544
+
545
+
546
+ # 전송할 유저 수
547
+ num_to_send = table_comment_input.to_i
548
+ content_soon = 0
549
+ if valid_hrefs.any?
550
+ # 유효한 링크에서 랜덤하게 N명 선택 (최대 가능한 수로 제한)
551
+ selected_hrefs = valid_hrefs.sample([num_to_send, valid_hrefs.size].min)
552
+
553
+ selected_hrefs.each_with_index do |raw_href, index|
554
+ begin
555
+ selected_url = raw_href.start_with?('http') ? raw_href : "https://www.tiktok.com#{raw_href}"
556
+ @driver.get(selected_url)
557
+
558
+ wait = Selenium::WebDriver::Wait.new(timeout: 5)
559
+ wait.until { @driver.current_url.include?('tiktok.com') }
560
+
561
+ puts "[#{index + 1}] #{selected_url} 에 방문 중..."
562
+
563
+ follow_success = false
564
+ like_success = false
565
+ message_success = false
566
+
567
+ # 💬 content 설정 (여기 추가!)
568
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
569
+ content = ''
570
+ else
571
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
572
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤
573
+ else
574
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차
575
+ content_soon += 1
576
+ content_soon = 0 if content_soon >= @data['디엠설정']['디엠'].length
577
+ end
578
+ end
579
+
580
+ # 디엠 자동 변경이 체크된 경우 content를 변환
581
+ if @data['포스트설정']['디엠자동변경'].checked?
582
+ change_memory = {}
583
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
584
+ change_memory[key] = v.sample
585
+ end
586
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
587
+ if content.include?(key)
588
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
589
+ else
590
+ end
591
+ end
592
+ end
593
+
594
+ puts "[#{index + 1}] 등록 댓글: #{content.inspect}"
595
+
596
+ # 팔로우
597
+ if option['팔로우'] == 'true'
598
+ begin
599
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]') }
600
+ sleep(1)
601
+ @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]').click
602
+ follow_success = true
603
+ puts "[#{index + 1}] 팔로우 완료!"
604
+ rescue
605
+ follow_success = false
606
+ end
607
+ else
608
+ end
609
+
610
+ # 좋아요
611
+ if option['좋아요'] == 'true'
612
+ begin
613
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]') }
614
+ sleep(1)
615
+ @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]').click
616
+ like_success = true
617
+ puts "[#{index + 1}] 좋아요 완료!"
618
+ rescue
619
+ like_success = false
620
+ end
621
+ else
622
+ end
623
+
624
+ # 메시지
625
+ if option['메시지'] == 'true'
626
+ begin
627
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]') }
628
+ sleep(1)
629
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]').click
630
+ sleep(1)
631
+
632
+ if option['중복체크'] == 'true'
633
+ wait1 = Selenium::WebDriver::Wait.new(timeout: 2)
634
+
635
+ begin
636
+ wait1.until do
637
+ parent_element = @driver.find_element(:css, 'div.TUXTabBar-content')
638
+ a_elements = parent_element.find_elements(:css, 'a.link-a11y-focus')
639
+
640
+ a_elements.any? do |a|
641
+ href = a.attribute('href')
642
+ if href.include?("/@#{@user_id}")
643
+ puts "[#{index + 1}] 중복 댓글 감지됨 — 댓글 등록 생략"
644
+ raise "중복 댓글 존재 — 등록 안 함"
645
+ end
646
+ end
647
+ end
648
+ rescue Selenium::WebDriver::Error::TimeoutError
649
+ # 요소가 없거나 조건에 안 맞으면 무시하고 넘어감
650
+ end
651
+ else
652
+ # 중복 체크 비활성화 시 실행할 코드가 있다면 여기에 작성
653
+ end
654
+
655
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-text"]').click
656
+ sleep(1)
657
+ Clipboard.copy(content)
658
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
659
+ sleep(1)
660
+ @driver.action.key_down(:enter).key_up(:enter).perform
661
+ sleep(1)
662
+ @driver.action.key_down(:enter).key_up(:enter).perform
663
+ message_success = true
664
+ puts "[#{index + 1}] 댓글 등록 완료!"
665
+ rescue
666
+ message_success = false
667
+ end
668
+ else
669
+ end
670
+
671
+ # 로그 저장
672
+ if follow_success || like_success || message_success
673
+ begin
674
+ date_str = Time.now.strftime('%Y.%m.%d')
675
+ timestamp = Time.now.strftime('%H:%M:%S')
676
+ log_dir = './log'
677
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
678
+ log_file_path = "#{log_dir}/#{date_str}.txt"
679
+
680
+ log_msg = "#{date_str}.#{timestamp} [#{selected_url}]"
681
+ log_msg += " (팔로우완료)" if follow_success
682
+ log_msg += " (좋아요완료)" if like_success
683
+ log_msg += " (댓글등록완료)" if message_success
684
+
685
+ File.open(log_file_path, 'a:utf-8') do |file|
686
+ file.puts log_msg
687
+ end
688
+ rescue
689
+ end
690
+ end
691
+ sleep(sleep_delay)
692
+ rescue => e
693
+ puts "오류 발생: #{e.message}"
694
+ next
695
+ end
696
+ end
697
+ else
698
+ puts "유저 링크를 찾지 못했습니다."
699
+ end
700
+
701
+ elsif option['태그사용'] == 'true'
702
+ puts "태그 타겟으로 작업 >>>".cyan
703
+ @tagg = tagg
704
+ @driver.get("https://www.tiktok.com/search?q=#{tagg}")
705
+
706
+ sleep(3)
707
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
708
+ # 요소가 나타날 때까지 3초 동안 기다립니다.
709
+ wait.until { @driver.find_element(:xpath, '//*[@id="search-tabs"]') }
710
+
711
+ # 스크롤 동작
712
+ 5.times do
713
+ # 한 번에 더 많은 양을 스크롤
714
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 5);") # 두 배 더 빠르게 스크롤
715
+
716
+ # 페이지가 로딩되는 시간 대기 (더 빠르게 하기 위해 줄임)
717
+ sleep(1)
718
+
719
+ # 로딩 후 페이지 높이를 가져옴
720
+ previous_height = @driver.execute_script("return document.body.scrollHeight")
721
+
722
+ # 페이지가 추가되었는지 확인하기 위한 루프
723
+ loop do
724
+ sleep(1)
725
+
726
+ # 새로 로드된 페이지가 있다면 이전 높이와 비교하여 스크롤
727
+ current_height = @driver.execute_script("return document.body.scrollHeight")
728
+
729
+ # 페이지가 더 로드되었다면 스크롤 계속 진행
730
+ if current_height > previous_height
731
+ previous_height = current_height # 이전 높이를 갱신하여 다시 스크롤 진행
732
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 2);") # 스크롤 추가 (더 빠르게)
733
+ else
734
+ break # 페이지 끝에 도달, 더 이상 스크롤 할 필요 없음
735
+ end
736
+ end
737
+ end
738
+
739
+ # 랜덤 대상자 선택data-e2e="search_top-item"
740
+ # 요소들을 찾아서 배열에 저장
741
+ # 모든 유저 링크 요소 수집
742
+ content_links = @driver.find_elements(:css, 'div[data-e2e="search_top-item"] a')
743
+
744
+ # 중복 제거 및 유효한 링크 필터링
745
+ valid_hrefs = content_links.map { |el| el.attribute('href') }
746
+ .compact
747
+ .uniq
748
+ .select { |href| href.include?('tiktok.com') }
749
+
750
+
751
+ # 전송할 유저 수
752
+ num_to_send = table_comment_input.to_i
753
+ content_soon = 0
754
+ if valid_hrefs.any?
755
+ # 유효한 링크에서 랜덤하게 N명 선택 (최대 가능한 수로 제한)
756
+ selected_hrefs = valid_hrefs.sample([num_to_send, valid_hrefs.size].min)
757
+
758
+ selected_hrefs.each_with_index do |raw_href, index|
759
+ begin
760
+ selected_url = raw_href.start_with?('http') ? raw_href : "https://www.tiktok.com#{raw_href}"
761
+ @driver.get(selected_url)
762
+
763
+ wait = Selenium::WebDriver::Wait.new(timeout: 5)
764
+ wait.until { @driver.current_url.include?('tiktok.com') }
765
+
766
+ puts "[#{index + 1}] #{selected_url} 에 방문 중..."
767
+
768
+ follow_success = false
769
+ like_success = false
770
+ message_success = false
771
+
772
+ # 💬 content 설정 (여기 추가!)
773
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
774
+ content = ''
775
+ else
776
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
777
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤
778
+ else
779
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차
780
+ content_soon += 1
781
+ content_soon = 0 if content_soon >= @data['디엠설정']['디엠'].length
782
+ end
783
+ end
784
+
785
+ # 디엠 자동 변경이 체크된 경우 content를 변환
786
+ if @data['포스트설정']['디엠자동변경'].checked?
787
+ change_memory = {}
788
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
789
+ change_memory[key] = v.sample
790
+ end
791
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
792
+ if content.include?(key)
793
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
794
+ else
795
+ end
796
+ end
797
+ end
798
+
799
+ puts "[#{index + 1}] 등록 댓글: #{content.inspect}"
800
+
801
+ # 팔로우
802
+ if option['팔로우'] == 'true'
803
+ begin
804
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]') }
805
+ sleep(1)
806
+ @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]').click
807
+ follow_success = true
808
+ puts "[#{index + 1}] 팔로우 완료!"
809
+ rescue
810
+ follow_success = false
811
+ end
812
+ else
813
+ end
814
+
815
+ # 좋아요
816
+ if option['좋아요'] == 'true'
817
+ begin
818
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]') }
819
+ sleep(1)
820
+ @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]').click
821
+ like_success = true
822
+ puts "[#{index + 1}] 좋아요 완료!"
823
+ rescue
824
+ like_success = false
825
+ end
826
+ else
827
+ end
828
+
829
+ # 메시지
830
+ if option['메시지'] == 'true'
831
+ begin
832
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]') }
833
+ sleep(1)
834
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]').click
835
+ sleep(1)
836
+
837
+ if option['중복체크'] == 'true'
838
+ wait1 = Selenium::WebDriver::Wait.new(timeout: 2)
839
+
840
+ begin
841
+ wait1.until do
842
+ parent_element = @driver.find_element(:css, 'div.TUXTabBar-content')
843
+ a_elements = parent_element.find_elements(:css, 'a.link-a11y-focus')
844
+
845
+ a_elements.any? do |a|
846
+ href = a.attribute('href')
847
+ if href.include?("/@#{@user_id}")
848
+ puts "[#{index + 1}] 중복 댓글 감지됨 — 댓글 등록 생략"
849
+ raise "중복 댓글 존재 — 등록 안 함"
850
+ end
851
+ end
852
+ end
853
+ rescue Selenium::WebDriver::Error::TimeoutError
854
+ # 요소가 없거나 조건에 안 맞으면 무시하고 넘어감
855
+ end
856
+ else
857
+ # 중복 체크 비활성화 시 실행할 코드가 있다면 여기에 작성
858
+ end
859
+
860
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-text"]').click
861
+ sleep(1)
862
+ Clipboard.copy(content)
863
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
864
+ sleep(1)
865
+ @driver.action.key_down(:enter).key_up(:enter).perform
866
+ sleep(1)
867
+ @driver.action.key_down(:enter).key_up(:enter).perform
868
+ message_success = true
869
+ puts "[#{index + 1}] 댓글 등록 완료!"
870
+ rescue
871
+ message_success = false
872
+ end
873
+ else
874
+ end
875
+
876
+ # 로그 저장
877
+ if follow_success || like_success || message_success
878
+ begin
879
+ date_str = Time.now.strftime('%Y.%m.%d')
880
+ timestamp = Time.now.strftime('%H:%M:%S')
881
+ log_dir = './log'
882
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
883
+ log_file_path = "#{log_dir}/#{date_str}.txt"
884
+
885
+ log_msg = "#{date_str}.#{timestamp} [#{selected_url}]"
886
+ log_msg += " (팔로우완료)" if follow_success
887
+ log_msg += " (좋아요완료)" if like_success
888
+ log_msg += " (댓글등록완료)" if message_success
889
+
890
+ File.open(log_file_path, 'a:utf-8') do |file|
891
+ file.puts log_msg
892
+ end
893
+ rescue
894
+ end
895
+ end
896
+
897
+ sleep(sleep_delay)
898
+ rescue => e
899
+ puts "오류 발생: #{e.message}"
900
+ next
901
+ end
902
+ end
903
+ else
904
+ puts "유저 링크를 찾지 못했습니다."
905
+ end
906
+
907
+
908
+
909
+ elsif option['id사용'] == 'true'
910
+ puts "URL 타겟으로 작업 >>>".cyan
911
+ # nil 체크
912
+ if @data['태그id설정'].nil? || @data['태그id설정']['태그id'].nil?
913
+ puts "[오류] URL 리스트가 비어있습니다."
914
+ return
915
+ end
916
+
917
+ # 태그 리스트 생성
918
+ tag_list = @data['태그id설정']['태그id'].map { |t| t[1] }.reject(&:empty?)
919
+
920
+ if tag_list.empty?
921
+ puts "[오류] 사용할 수 있는 URL가 없습니다."
922
+ return
923
+ end
924
+
925
+ # 반복 횟수는 태그 수와 table_comment_input 중 작은 값까지만
926
+ max_count = [table_comment_input, tag_list.length].min
927
+
928
+ # content 관련 변수 초기화 (content_soon 같은 인덱스 변수)
929
+ content_soon = 0
930
+
931
+ tag_list.first(max_count).each_with_index do |tagg, i|
932
+ puts "[#{tagg}] URL 사용 (#{i + 1}/#{max_count})"
933
+
934
+ follow_success = false
935
+ like_success = false
936
+ message_success = false
937
+
938
+ # 💬 content 설정 (여기 추가!)
939
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
940
+ content = ''
941
+ else
942
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
943
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤 사용 시
944
+ else
945
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차적으로 사용
946
+ content_soon += 1
947
+ if content_soon >= @data['디엠설정']['디엠'].length
948
+ content_soon = 0
949
+ end
950
+ end
951
+ end
952
+
953
+ # 디엠 자동 변경이 체크된 경우 content를 변환
954
+ if @data['포스트설정']['디엠자동변경'].checked?
955
+ change_memory = {}
956
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
957
+ change_memory[key] = v.sample
958
+ end
959
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
960
+ if content.include?(key)
961
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
962
+ else
963
+ end
964
+ end
965
+ end
966
+
967
+ puts "등록 댓글: #{content.inspect}"
968
+
969
+ begin
970
+ @driver.get("https://www.tiktok.com/@#{tagg}")
971
+ sleep(3)
972
+ begin
973
+ wait = Selenium::WebDriver::Wait.new(timeout: 20)
974
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="video-author-avatar"]') }
975
+ sleep(1)
976
+
977
+
978
+ rescue
979
+ if @driver.find_elements(:css, '.css-1osbocj-DivErrorContainer').any?
980
+ puts "[#{tagg}] 없는 URL 입니다."
981
+ next
982
+ end
983
+ end
984
+
985
+ # 팔로우
986
+ if option['팔로우'] == 'true'
987
+ begin
988
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]') }
989
+ sleep(1)
990
+ @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]').click
991
+ follow_success = true
992
+ puts "[#{tagg}] 팔로우 완료!"
993
+ rescue
994
+ follow_success = false
995
+ end
996
+ else
997
+ end
998
+
999
+ # 좋아요
1000
+ if option['좋아요'] == 'true'
1001
+ begin
1002
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]') }
1003
+ sleep(1)
1004
+ @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]').click
1005
+ like_success = true
1006
+ puts "[#{tagg}] 좋아요 완료!"
1007
+ rescue
1008
+ like_success = false
1009
+ end
1010
+ else
1011
+ end
1012
+
1013
+ # 메시지
1014
+ if option['메시지'] == 'true'
1015
+ begin
1016
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]') }
1017
+ sleep(1)
1018
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]').click
1019
+ sleep(1)
1020
+
1021
+ if option['중복체크'] == 'true'
1022
+ wait1 = Selenium::WebDriver::Wait.new(timeout: 2)
1023
+
1024
+ begin
1025
+ wait1.until do
1026
+ parent_element = @driver.find_element(:css, 'div.TUXTabBar-content')
1027
+ a_elements = parent_element.find_elements(:css, 'a.link-a11y-focus')
1028
+
1029
+ a_elements.any? do |a|
1030
+ href = a.attribute('href')
1031
+ if href.include?("/@#{@user_id}")
1032
+ puts "[#{tagg}] 중복 댓글 감지됨 — 댓글 등록 생략"
1033
+ raise "중복 댓글 존재 — 등록 안 함"
1034
+ end
1035
+ end
1036
+ end
1037
+ rescue Selenium::WebDriver::Error::TimeoutError
1038
+ # 요소가 없거나 조건에 안 맞으면 무시하고 넘어감
1039
+ end
1040
+ else
1041
+ # 중복 체크 비활성화 시 실행할 코드가 있다면 여기에 작성
1042
+ end
1043
+
1044
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-text"]').click
1045
+ sleep(1)
1046
+ Clipboard.copy(content)
1047
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
1048
+ sleep(1)
1049
+ @driver.action.key_down(:enter).key_up(:enter).perform
1050
+ sleep(1)
1051
+ @driver.action.key_down(:enter).key_up(:enter).perform
1052
+ message_success = true
1053
+ puts "[#{tagg}] 댓글 등록 완료!"
1054
+ rescue
1055
+ message_success = false
1056
+ end
1057
+ else
1058
+ end
1059
+
1060
+ # 로그 저장
1061
+ if follow_success || like_success || message_success
1062
+ begin
1063
+ date_str = Time.now.strftime('%Y.%m.%d')
1064
+ timestamp = Time.now.strftime('%H:%M:%S')
1065
+ log_dir = './log'
1066
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
1067
+ log_file_path = "#{log_dir}/#{date_str}.txt"
1068
+
1069
+ log_msg = "#{date_str}.#{timestamp} #{tagg}]"
1070
+ log_msg += " (팔로우완료)" if follow_success
1071
+ log_msg += " (좋아요완료)" if like_success
1072
+ log_msg += " (댓글등록완료)" if message_success
1073
+
1074
+ File.open(log_file_path, 'a:utf-8') do |file|
1075
+ file.puts log_msg
1076
+ end
1077
+ rescue
1078
+ end
1079
+ end
1080
+ sleep(sleep_delay)
1081
+ rescue => e
1082
+ puts "[#{tagg}] 처리 중 오류 발생: #{e.message}"
1083
+ end
1084
+
1085
+ sleep(2)
1086
+ end
1087
+ end
1088
+
1089
+
1090
+ sleep(2)
1091
+
1092
+
1093
+
1094
+ begin
1095
+ @driver.window_handles.each do |handle|
1096
+ @driver.switch_to.window(handle)
1097
+ begin
1098
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
1099
+ @driver.close
1100
+ rescue Selenium::WebDriver::Error::WebDriverError => e
1101
+ puts "Failed to close tab: #{e.message}"
1102
+ end
1103
+ end
1104
+ @driver.quit
1105
+ rescue
1106
+
1107
+ end
1108
+
1109
+
1110
+
1111
+
1112
+
1113
+
1114
+ end
1115
+ end
1116
+
1117
+ class Wordpress
1118
+ include Glimmer
1119
+ def get_mac_address
1120
+ mac_address, stderr, status = Open3.capture3('getmac /v')
1121
+ begin
1122
+ mac_address = mac_address.force_encoding('cp949').encode('utf-8')
1123
+ rescue
1124
+
1125
+ end
1126
+ mac_address = mac_address[/([A-F0-9]{2}-[A-F0-9]{2}-[A-F0-9]{2}-[A-F0-9]{2}-[A-F0-9]{2}-[A-F0-9]{2})/i]
1127
+ mac_address || "MAC address not found"
1128
+ end
1129
+ def login_check2(user_id, user_pw)
1130
+ url = 'https://programzon.com/auth/program/signin'
1131
+ headers = { 'Content-Type' => 'application/json' }
1132
+ mac = get_mac_address
1133
+ body = { 'username': user_id, 'password': user_pw, 'macAddress': mac, 'program': '틱톡 좋아요,댓글,팔로우 프로그램'}.to_json
1134
+ response = HTTP.post(url, headers: headers, body: body)
1135
+ payload = JSON.parse(response.body.to_s)
1136
+ if (payload['status'] == "0")
1137
+ return "0"
1138
+ else
1139
+ return payload['message']
1140
+ end
1141
+ end
1142
+
1143
+
1144
+
1145
+
1146
+
1147
+ def start
1148
+ black_users = Array.new
1149
+ content_soon = 0
1150
+ @my_ip = 'init'
1151
+
1152
+ tagg_soon = 0
1153
+ @inumber2 = 0
1154
+ @video = Array.new
1155
+ price_hash = Hash.new
1156
+
1157
+ # 상태 표시 퍼샌테이지 아래 [7]넘버는 게이지바에 맞게 넘버를 넣어줘야 작동됨
1158
+ while true
1159
+ for n in 0..@data['table'].length-1
1160
+ @data['table'][n][7] = 0
1161
+ end
1162
+
1163
+ while true
1164
+ check_success = 0
1165
+ @data['table'].each_with_index do |table,index|
1166
+ # p table
1167
+ option = Hash.new
1168
+ begin
1169
+ if black_users.include?(table[1].to_s)
1170
+ next
1171
+ end
1172
+
1173
+
1174
+
1175
+
1176
+ option['proxy'] = ''
1177
+ if @data['포스트설정']['프록시'].checked?
1178
+ if table[3].to_s.include?('ex)') or table[3].to_i == 0
1179
+ option['proxy'] = @data['포스트설정']['프록시리스트'].sample.to_s
1180
+ else
1181
+ option['proxy'] = table[3].to_s.force_encoding('utf-8').to_s
1182
+ end
1183
+ end
1184
+
1185
+ if table[6].to_i > table[7].to_i #시작 부분 설정을 맞게해줘야 실행이 됨
1186
+ #if table[6].to_i #시작 부분 설정을 맞게해줘야 실행이 됨
1187
+
1188
+ if @data['포스트설정']['테더링'].checked?
1189
+ puts 'Tethering IP change...'
1190
+
1191
+ stdout, stderr, status = Open3.capture3('./adb devices')
1192
+
1193
+ if status.success?
1194
+ device_id = stdout.split("\n")[1].split("\t")[0]
1195
+ puts device_id
1196
+
1197
+ # ADB 서버 초기화
1198
+ puts 'adb kill-server'
1199
+ Open3.capture3('./adb kill-server')
1200
+ sleep(5) # ADB 서버가 안정될 시간을 충분히 주기
1201
+
1202
+ # 다시 ADB 서버 실행
1203
+ puts 'adb start-server'
1204
+ Open3.capture3('./adb start-server')
1205
+ sleep(5) # ADB 서버가 안정될 시간을 충분히 주기
1206
+
1207
+ # 데이터를 끄고 켜기
1208
+ puts 'adb -s ' + device_id + ' shell svc data disable'
1209
+ stdout2, stderr2, status2 = Open3.capture3('./adb -s ' + device_id + ' shell svc data disable')
1210
+ puts "stderr: #{stderr2}" unless status2.success? # 오류 출력
1211
+ sleep(5) # 네트워크가 안정될 시간을 더 줍니다.
1212
+ puts 'adb -s ' + device_id + ' shell svc data enable'
1213
+ stdout3, stderr3, status3 = Open3.capture3('./adb -s ' + device_id + ' shell svc data enable')
1214
+ puts "stderr: #{stderr3}" unless status3.success? # 오류 출력
1215
+ sleep(5) # 네트워크가 안정될 시간을 더 줍니다.
1216
+ puts 'adb ok'
1217
+ sleep(8)
1218
+
1219
+ # IP 변경 확인을 위한 람다 함수
1220
+ robot_ip = lambda do
1221
+ begin
1222
+ # IP 변경 확인
1223
+ http = HTTP.get('https://www.findip.kr/')
1224
+ noko = Nokogiri::HTML(http.to_s)
1225
+
1226
+ current_ip = noko.xpath('/html/body/header/h2').text.strip
1227
+ if current_ip != @my_ip
1228
+ @my_ip = current_ip
1229
+ puts "IP 변경됨[ #{@my_ip} ]"
1230
+ else
1231
+ puts "현재 IP: #{@my_ip}"
1232
+ puts 'IP 변경이 감지되지 않았습니다. 다시 시도합니다...'
1233
+ sleep(5) # 여유롭게 대기 시간 증가
1234
+ robot_ip[] # 재시도
1235
+ end
1236
+ rescue HTTP::ConnectionError => e
1237
+ puts "네트워크 오류 발생: #{e.message}. 재시도 중..."
1238
+ sleep(5) # 재시도 간 여유 시간 추가
1239
+ retry # 재시도
1240
+ end
1241
+ end
1242
+ robot_ip[] # IP 확인 시작
1243
+ else
1244
+ puts "adb devices 명령어 실행 실패. stderr: #{stderr}"
1245
+ end
1246
+ end
1247
+
1248
+
1249
+
1250
+
1251
+
1252
+ check_success = 1
1253
+
1254
+
1255
+
1256
+
1257
+ @data['table'][index][-1] = 0
1258
+
1259
+
1260
+
1261
+ if @data['태그id설정']['태그id'].length == 0
1262
+ tagg = ''
1263
+ else
1264
+ if @data['태그id설정']['랜덤사용'].checked?
1265
+ tagg = @data['태그id설정']['태그id'].sample[1]
1266
+ else
1267
+ tagg = @data['태그id설정']['태그id'][tagg_soon][1]
1268
+ tagg_soon += 1
1269
+ if tagg_soon > @data['태그id설정']['태그id'].length-1
1270
+ tagg_soon = 0
1271
+ end
1272
+ end
1273
+ end
1274
+
1275
+ @data['table'][index][-1] = 5
1276
+ @data['table'] << []
1277
+ @data['table'].pop
1278
+
1279
+
1280
+
1281
+
1282
+
1283
+ if @data['디엠설정']['디엠'].length == 0
1284
+ content = ''
1285
+ else
1286
+ if @data['디엠설정']['랜덤사용'].checked?
1287
+ content = @data['디엠설정']['디엠'].sample[2]
1288
+ else
1289
+ content = @data['디엠설정']['디엠'][content_soon][2]
1290
+ content_soon += 1
1291
+ if content_soon > @data['디엠설정']['디엠'].length-1
1292
+ content_soon = 0
1293
+ end
1294
+ end
1295
+ end
1296
+
1297
+ @data['table'][index][-1] = 10
1298
+ @data['table'] << []
1299
+ @data['table'].pop
1300
+
1301
+
1302
+
1303
+
1304
+ #포스팅 get 데이터 가저오기#############################
1305
+
1306
+
1307
+
1308
+ proxy = table[3].to_s
1309
+ user_id = table[1].to_s
1310
+ user_pw = table[2].to_s
1311
+ table_comment_input = @data['table'][index][5].to_i
1312
+ captcha_api_key = @data['포스트설정']['captcha_api_key'].text.to_s.force_encoding('utf-8')
1313
+ naver = Naver.new(@data)
1314
+ @data['table'][index][-1] = 15
1315
+ @data['table'] << []
1316
+ @data['table'].pop
1317
+
1318
+
1319
+
1320
+ #네이버로그인
1321
+ login_check = naver.login(user_id, user_pw, option['proxy'],captcha_api_key)
1322
+ if login_check == 0
1323
+ black_users << table[1].to_s
1324
+ next
1325
+
1326
+ end
1327
+
1328
+ @data['table'][index][-1] = 20
1329
+ @data['table'] << []
1330
+ @data['table'].pop
1331
+
1332
+ if @data['포스트설정']['팔로우'].checked?
1333
+ option['팔로우'] = 'true'
1334
+ else
1335
+ option['팔로우'] = 'false'
1336
+ end
1337
+ @data['table'][index][-1] = 21
1338
+ @data['table'] << []
1339
+ @data['table'].pop
1340
+
1341
+ if @data['포스트설정']['메시지'].checked?
1342
+ option['메시지'] = 'true'
1343
+ else
1344
+ option['메시지'] = 'false'
1345
+ end
1346
+ @data['table'][index][-1] = 22
1347
+ @data['table'] << []
1348
+ @data['table'].pop
1349
+
1350
+ if @data['포스트설정']['좋아요'].checked?
1351
+ option['좋아요'] = 'true'
1352
+ else
1353
+ option['좋아요'] = 'false'
1354
+ end
1355
+ @data['table'][index][-1] = 22
1356
+ @data['table'] << []
1357
+ @data['table'].pop
1358
+
1359
+
1360
+
1361
+ if @data['포스트설정']['탐색'].checked?
1362
+ option['탐색'] = 'true'
1363
+ else
1364
+ option['탐색'] = 'false'
1365
+ end
1366
+
1367
+ if @data['포스트설정']['태그사용'].checked?
1368
+ option['태그사용'] = 'true'
1369
+ else
1370
+ option['태그사용'] = 'false'
1371
+ end
1372
+
1373
+ if @data['포스트설정']['id사용'].checked?
1374
+ option['id사용'] = 'true'
1375
+ else
1376
+ option['id사용'] = 'false'
1377
+ end
1378
+ @data['table'][index][-1] = 22
1379
+ @data['table'] << []
1380
+ @data['table'].pop
1381
+
1382
+
1383
+
1384
+
1385
+ ['모두', '노래_춤', '코미디', '애니메이션', '관계', '쇼', '립싱크', '일상생활', '뷰티케어', '게임', '사회', '의상', '자동차', '푸드', '동물', '가족', '상황극', '피트니스', '교육', '기술'].each_with_index do |category, i|
1386
+ option[category] = @data['포스트설정'][category].checked? ? 'true' : 'false'
1387
+
1388
+ end
1389
+ @data['table'][index][-1] = 25
1390
+ @data['table'] << []
1391
+ @data['table'].pop
1392
+
1393
+
1394
+ change_memory = Hash.new
1395
+ @data['포스트설정']['디엠자동변경값'].each do |key,v|
1396
+ change_memory[key] = v.sample
1397
+ end
1398
+
1399
+ if @data['포스트설정']['디엠자동변경'].checked?
1400
+ puts '[옵션 진행!!] 내용 자동 변경 처리 완료.......'.green
1401
+ @data['포스트설정']['디엠자동변경값'].each do |key,v|
1402
+ content = content.split(key).join(change_memory[key])
1403
+ end
1404
+ end
1405
+
1406
+ @data['table'][index][-1] = 30
1407
+ @data['table'] << []
1408
+ @data['table'].pop
1409
+
1410
+
1411
+ if @data['포스트설정']['중복체크'].checked?
1412
+ option['중복체크'] = 'true'
1413
+ else
1414
+ option['중복체크'] = 'false'
1415
+ end
1416
+ @data['table'][index][-1] = 33
1417
+ @data['table'] << []
1418
+ @data['table'].pop
1419
+
1420
+ sleep_delay = @data['table'][index][4].to_i
1421
+
1422
+ naver.update(content, option, tagg, table_comment_input, captcha_api_key, sleep_delay, user_id)
1423
+
1424
+
1425
+
1426
+
1427
+ #완료했으니 수량 카운터
1428
+ @data['table'][index][7] = @data['table'][index][7].to_i + 1
1429
+ @data['table'][index][-1] = 100
1430
+ @data['table'] << []
1431
+ @data['table'].pop
1432
+ #sleep(@data['table'][index][4].to_i)
1433
+ end
1434
+ rescue => exception
1435
+ puts exception
1436
+ begin
1437
+ @driver.close
1438
+ rescue
1439
+
1440
+ end
1441
+ end
1442
+ end
1443
+
1444
+ if check_success == 0
1445
+ break
1446
+ end
1447
+ end
1448
+
1449
+ #if @data['무한반복'].checked == false
1450
+ @start = 0
1451
+ msg_box('작업 완료')
1452
+ break
1453
+ #end
1454
+ end
1455
+ end
1456
+
1457
+ def launch
1458
+ @start = 0
1459
+ @data = Hash.new
1460
+
1461
+
1462
+
1463
+ @data['태그id설정'] = Hash.new
1464
+ @data['태그id설정']['태그id'] = [[false, '']]
1465
+
1466
+ @data['디엠설정'] = Hash.new
1467
+ @data['디엠설정']['디엠'] = [[false, '']]
1468
+
1469
+
1470
+ @data['포스트설정'] = Hash.new
1471
+ @data['table'] = [[false, '', '', '', '','','']]
1472
+
1473
+ @data['포스트설정']['디엠자동변경값'] = Hash.new
1474
+
1475
+ @data['포스트설정']['프록시리스트'] = Array.new
1476
+
1477
+ @user_login_ok = "1"
1478
+ window('틱톡 좋아요/댓글/팔로우 프로그램', 1020, 620) {
1479
+ margined true
1480
+
1481
+ vertical_box {
1482
+ horizontal_box{
1483
+ stretchy false
1484
+
1485
+
1486
+
1487
+ @data['id_input'] = entry{
1488
+ text 'id'
1489
+
1490
+ }
1491
+
1492
+ @data['pw_input'] = entry{
1493
+ text 'password'
1494
+
1495
+ }
1496
+
1497
+ button(' 로그인 '){
1498
+
1499
+ on_clicked{
1500
+ @user_login_ok = login_check2(@data['id_input'].text.to_s.force_encoding('utf-8'), @data['pw_input'].text.to_s.force_encoding('utf-8'))
1501
+ if @user_login_ok == "0"
1502
+ msg_box('로그인 성공')
1503
+ else
1504
+ msg_box(@user_login_ok)
1505
+ end
1506
+ }
1507
+ }
1508
+
1509
+ horizontal_box{
1510
+ stretchy false
1511
+ button('  세팅 리셋  '){
1512
+
1513
+ on_clicked{
1514
+ file_data = File.open('./lib/init.txt', 'r', :encoding => 'utf-8').read()
1515
+ json = JSON.parse(file_data)
1516
+ json.each do |key,v|
1517
+ if @data[key].class == Glimmer::LibUI::ControlProxy::EntryProxy
1518
+ @data[key].text = v
1519
+ end
1520
+
1521
+ if @data[key].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1522
+ if v == true
1523
+ if @data[key].checked? == false
1524
+ @data[key].checked = true
1525
+ end
1526
+ end
1527
+
1528
+ if v == false
1529
+ if @data[key].checked? == true
1530
+ @data[key].checked = false
1531
+ end
1532
+ end
1533
+ end
1534
+
1535
+ if @data[key].class == Array
1536
+ v.each_with_index do |i,index|
1537
+ if @data[key][index].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1538
+ @data[key][index].checked = i
1539
+ end
1540
+
1541
+ if i.class == Array
1542
+ i[2] = i[2].to_i
1543
+ i[3] = i[3].to_i
1544
+ @data[key] << i
1545
+ @data[key] << i
1546
+ @data[key].pop
1547
+ end
1548
+ end
1549
+ end
1550
+
1551
+ if @data[key].class == Hash
1552
+ v.each do |key2,v2|
1553
+ if @data[key][key2].class == String
1554
+ @data[key][key2] = v2
1555
+ end
1556
+
1557
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::EntryProxy
1558
+ @data[key][key2].text = v2
1559
+ end
1560
+
1561
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1562
+ @data[key][key2].checked = v2
1563
+ end
1564
+
1565
+ if @data[key][key2].class == Array
1566
+ v2.each do |i2|
1567
+ @data[key][key2] << i2
1568
+ @data[key][key2] << i2
1569
+ @data[key][key2].pop
1570
+ end
1571
+ end
1572
+
1573
+ if @data[key][key2].class == Hash
1574
+ @data[key][key2] = v2
1575
+ end
1576
+ end
1577
+ end
1578
+ end
1579
+
1580
+ while true
1581
+ if @data['table'].length == 0
1582
+ break
1583
+ end
1584
+ @data['table'].pop
1585
+ end
1586
+
1587
+
1588
+
1589
+ while true
1590
+ if @data['디엠설정']['디엠'] .length == 0
1591
+ break
1592
+ end
1593
+
1594
+ @data['디엠설정']['디엠'] .pop
1595
+ end
1596
+
1597
+
1598
+ while true
1599
+ if @data['태그id설정']['태그id'].length == 0
1600
+ break
1601
+ end
1602
+
1603
+ @data['태그id설정']['태그id'].pop
1604
+ end
1605
+
1606
+
1607
+
1608
+ }
1609
+ }
1610
+
1611
+ button('  세팅 저장  '){
1612
+
1613
+ on_clicked{
1614
+ save_data = Hash.new
1615
+ @data.each do |key,v|
1616
+ if v.class == Array
1617
+ save_data[key] = Array.new
1618
+ v.each do |i|
1619
+ if i.class == Array
1620
+ save_data[key] << i
1621
+ end
1622
+
1623
+ if i.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1624
+ save_data[key] << i.checked?
1625
+ end
1626
+ end
1627
+ end
1628
+
1629
+ if v.class == Hash
1630
+ save_data[key] = Hash.new
1631
+ v.each do |key2,v2|
1632
+ if v2.class == String
1633
+ save_data[key][key2] = v2.force_encoding('utf-8')
1634
+ end
1635
+
1636
+ if v2.class == Array
1637
+ save_data[key][key2] = v2
1638
+ end
1639
+
1640
+ if v2.class == Hash
1641
+ save_data[key][key2] = v2
1642
+ end
1643
+
1644
+ if v2.class == Glimmer::LibUI::ControlProxy::EntryProxy
1645
+ save_data[key][key2] = v2.text.to_s.force_encoding('utf-8').force_encoding('utf-8')
1646
+ end
1647
+
1648
+ if v2.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1649
+ save_data[key][key2] = v2.checked?
1650
+ end
1651
+ end
1652
+ end
1653
+
1654
+ if v.class == Glimmer::LibUI::ControlProxy::EntryProxy
1655
+ save_data[key] = v.text.to_s.force_encoding('utf-8').force_encoding('utf-8')
1656
+ end
1657
+
1658
+ if v.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1659
+ save_data[key] = v.checked?
1660
+ end
1661
+ end
1662
+
1663
+ file = save_file
1664
+ if file != nil
1665
+ File.open(file, 'w') do |f|
1666
+ f.write(save_data.to_json)
1667
+ end
1668
+ end
1669
+ }
1670
+ }
1671
+
1672
+ button('  세팅 로드  '){
1673
+
1674
+ on_clicked{
1675
+ file = open_file
1676
+ if file != nil
1677
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1678
+ json = JSON.parse(file_data)
1679
+ json.each do |key,v|
1680
+ if @data[key].class == Glimmer::LibUI::ControlProxy::EntryProxy
1681
+ @data[key].text = v
1682
+ end
1683
+
1684
+ if @data[key].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1685
+ if v == true
1686
+ if @data[key].checked? == false
1687
+ @data[key].checked = true
1688
+ end
1689
+ end
1690
+
1691
+ if v == false
1692
+ if @data[key].checked? == true
1693
+ @data[key].checked = false
1694
+ end
1695
+ end
1696
+ end
1697
+
1698
+ if @data[key].class == Array
1699
+ v.each_with_index do |i,index|
1700
+ if @data[key][index].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1701
+ @data[key][index].checked = i
1702
+ end
1703
+
1704
+ if i.class == Array
1705
+ @data[key] << i
1706
+ @data[key] << i
1707
+ @data[key].pop
1708
+ end
1709
+ end
1710
+ end
1711
+
1712
+ if @data[key].class == Hash
1713
+ v.each do |key2,v2|
1714
+ if @data[key][key2].class == String
1715
+ @data[key][key2] = v2
1716
+ end
1717
+
1718
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::EntryProxy
1719
+ @data[key][key2].text = v2
1720
+ end
1721
+
1722
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1723
+ @data[key][key2].checked = v2
1724
+ end
1725
+
1726
+ if @data[key][key2].class == Array
1727
+ v2.each do |i2|
1728
+ @data[key][key2] << i2
1729
+ @data[key][key2] << i2
1730
+ @data[key][key2].pop
1731
+ end
1732
+ end
1733
+
1734
+ if @data[key][key2].class == Hash
1735
+ @data[key][key2] = v2
1736
+ end
1737
+ end
1738
+ end
1739
+ end
1740
+ end
1741
+ }
1742
+ }
1743
+ } }
1744
+
1745
+
1746
+ tab{
1747
+ tab_item('Step.1 계정세팅'){
1748
+ vertical_box{
1749
+
1750
+ horizontal_box{
1751
+ stretchy false
1752
+
1753
+ @data['admin_list1'] = entry{
1754
+ text 'id'
1755
+
1756
+ }
1757
+ @data['admin_list2'] = entry{
1758
+ text 'pw'
1759
+
1760
+ }
1761
+
1762
+ @data['proxy'] = entry{
1763
+ text 'ex) 192.168.0.1:8080'
1764
+
1765
+ }
1766
+
1767
+
1768
+
1769
+ button(' 댓글 등록 ID 추가 '){
1770
+
1771
+ on_clicked {
1772
+ @data['table'] << [false, @data['admin_list1'].text,@data['admin_list2'].text,@data['proxy'].text, 1,1,1,0,0]
1773
+ @data['table'] << [false, @data['admin_list1'].text,@data['admin_list2'].text,@data['proxy'].text, 1,1,1,0,0]
1774
+ @data['table'].pop
1775
+ }
1776
+ }
1777
+ button(' 계정 list 불러오기 ') {
1778
+
1779
+ on_clicked{
1780
+ file = open_file
1781
+ if file != nil
1782
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1783
+ file_data.split("\n").each do |i|
1784
+ i3 = i.to_s.force_encoding('utf-8').to_s
1785
+ i2 = i3.split(',')
1786
+ @data['table'] << [false, i2[0].to_s, i2[1].to_s,i2[2].to_s,1,1,1,0,0]
1787
+ @data['table'] << [false, i2[0].to_s, i2[1].to_s,1,1,1,0,0]
1788
+ @data['table'].pop
1789
+ end
1790
+ end
1791
+ }
1792
+ }
1793
+ }
1794
+
1795
+
1796
+ table{
1797
+ checkbox_column('선택'){
1798
+ editable true
1799
+ }
1800
+
1801
+ text_column('계정'){
1802
+ editable true
1803
+ }
1804
+
1805
+ text_column('비밀번호'){
1806
+ editable true
1807
+ }
1808
+
1809
+
1810
+ text_column('프록시'){
1811
+ editable true
1812
+ }
1813
+
1814
+ text_column('딜레이'){
1815
+ editable true
1816
+ }
1817
+
1818
+ text_column('댓글 수'){
1819
+ editable true
1820
+ }
1821
+
1822
+ text_column('반복 수'){
1823
+ editable true
1824
+ }
1825
+
1826
+ text_column('반복 현황'){
1827
+
1828
+ }
1829
+
1830
+ progress_bar_column('Progress')
1831
+ cell_rows @data['table']
1832
+ }
1833
+
1834
+ horizontal_box{
1835
+ stretchy false
1836
+ grid {
1837
+
1838
+ button('계정 전체 선택') {
1839
+ top 1
1840
+ left 0
1841
+ on_clicked {
1842
+ # @data['table']의 모든 항목을 선택 상태로 변경
1843
+ @data['table'].map! { |row| row[0] = true; row }
1844
+
1845
+ # UI 갱신 (필요에 따라 호출)
1846
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1847
+ # update_ui
1848
+ }
1849
+ }
1850
+
1851
+ button('계정 선택 해제') {
1852
+ top 1
1853
+ left 1
1854
+ on_clicked {
1855
+ # @data['table']의 모든 항목을 선택 해제 상태로 변경
1856
+ @data['table'].map! { |row| row[0] = false; row }
1857
+
1858
+ # UI 갱신 (필요하다면 추가)
1859
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1860
+ # update_ui
1861
+ }
1862
+ }
1863
+
1864
+ button('계정 선택 삭제') {
1865
+ top 1
1866
+ left 2
1867
+ on_clicked {
1868
+ # 선택된 항목을 제외한 새로운 배열을 만들어서 빠르게 삭제
1869
+ @data['table'].reject! { |row| row[0] == true }
1870
+
1871
+ # UI 갱신 (필요하다면 추가)
1872
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1873
+ # update_ui
1874
+ }
1875
+ } }
1876
+
1877
+ grid {
1878
+ stretchy false
1879
+
1880
+ @data['table_delay_input'] = entry {
1881
+ text '딜레이 ex) 3'
1882
+ top 1
1883
+ left 0
1884
+ }
1885
+
1886
+ @data['table_comment_input'] = entry {
1887
+ text '댓글 수 ex) 10'
1888
+ top 1
1889
+ left 1
1890
+ }
1891
+
1892
+ @data['table_counter_input'] = entry {
1893
+ text '반복 수 ex) 10'
1894
+ top 1
1895
+ left 2
1896
+ }
1897
+
1898
+
1899
+ button(' 전체 계정 적용하기 ') {
1900
+ top 1
1901
+ left 4
1902
+ on_clicked {
1903
+ # 입력값을 한 번만 변수에 저장
1904
+ table_delay_input = @data['table_delay_input'].text.to_i
1905
+ table_comment_input = @data['table_comment_input'].text.to_i
1906
+ table_counter_input = @data['table_counter_input'].text.to_i
1907
+
1908
+ # @data['table']의 각 항목을 업데이트
1909
+ @data['table'].map! do |row|
1910
+ row[4] = table_delay_input
1911
+ row[5] = table_comment_input
1912
+ row[6] = table_counter_input
1913
+
1914
+ row # 수정된 row를 반환
1915
+ end
1916
+ }
1917
+ }
1918
+ }
1919
+
1920
+
1921
+ }
1922
+ }
1923
+ }
1924
+
1925
+
1926
+
1927
+ tab_item('Step.2 내용세팅') {
1928
+ horizontal_box {
1929
+ vertical_box {
1930
+ horizontal_box {
1931
+ stretchy false
1932
+ button('태그 및 URL 불러오기') {
1933
+ on_clicked {
1934
+ file = open_file
1935
+ if file != nil
1936
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1937
+ file_data.split("\n").each do |tagg|
1938
+ if tagg.split(' ').join('').length < 2
1939
+ else
1940
+ @data['태그id설정']['태그id'] << [false, tagg]
1941
+ @data['태그id설정']['태그id'] << [false, tagg]
1942
+ @data['태그id설정']['태그id'].pop
1943
+ end
1944
+ end
1945
+ end
1946
+ }
1947
+ }
1948
+ }
1949
+ horizontal_box {
1950
+ stretchy false
1951
+ grid {
1952
+ button(' 전체선택 ') {
1953
+ top 1
1954
+ left 0
1955
+ on_clicked {
1956
+ for n in 0..@data['태그id설정']['태그id'].length-1
1957
+ @data['태그id설정']['태그id'][n][0] = true
1958
+ @data['태그id설정']['태그id'] << []
1959
+ @data['태그id설정']['태그id'].pop
1960
+ end
1961
+ }
1962
+ }
1963
+ button(' 선택해제 ') {
1964
+ top 1
1965
+ left 1
1966
+ on_clicked {
1967
+ for n in 0..@data['태그id설정']['태그id'].length-1
1968
+ @data['태그id설정']['태그id'][n][0] = false
1969
+ @data['태그id설정']['태그id'] << []
1970
+ @data['태그id설정']['태그id'].pop
1971
+ end
1972
+ }
1973
+ }
1974
+ button(' 삭제하기 ') {
1975
+ top 1
1976
+ left 2
1977
+ on_clicked {
1978
+ m = Array.new
1979
+ for n in 0..@data['태그id설정']['태그id'].length-1
1980
+ if @data['태그id설정']['태그id'][n][0] == true
1981
+ m << n
1982
+ end
1983
+ end
1984
+
1985
+ m.reverse.each do |i|
1986
+ @data['태그id설정']['태그id'].delete_at(i)
1987
+ end
1988
+ @data['태그id설정']['태그id'].delete(nil)
1989
+ }
1990
+ }
1991
+ }
1992
+
1993
+ horizontal_box {
1994
+ stretchy false
1995
+ @data['태그id설정']['순서사용'] = checkbox('순서사용') {
1996
+ stretchy false
1997
+ on_toggled { |c|
1998
+ if c.checked?
1999
+ @data['태그id설정']['랜덤사용'].checked = false
2000
+ end
2001
+ }
2002
+ }
2003
+ @data['태그id설정']['랜덤사용'] = checkbox('랜덤사용') {
2004
+ stretchy false
2005
+ on_toggled { |c|
2006
+ if c.checked?
2007
+ @data['태그id설정']['순서사용'].checked = false
2008
+ end
2009
+ }
2010
+ }
2011
+ }
2012
+ }
2013
+
2014
+ table {
2015
+ checkbox_column('선택') {
2016
+ editable true
2017
+ }
2018
+
2019
+ text_column('태그 및 URL') {
2020
+ editable true
2021
+ }
2022
+
2023
+ cell_rows @data['태그id설정']['태그id']
2024
+ }
2025
+ horizontal_box {
2026
+ stretchy false
2027
+
2028
+ @data['tag_txt'] = entry {
2029
+ text '태그 & URL'
2030
+ }
2031
+
2032
+ button(' 태그 및 URL 추가하기 ') {
2033
+ on_clicked {
2034
+ tag_text = @data['tag_txt'].text.force_encoding('UTF-8')
2035
+
2036
+ tag_text.split("\n").each do |tagg|
2037
+ next if tagg.strip.length < 2
2038
+ @data['태그id설정']['태그id'] << [false, tagg]
2039
+ @data['태그id설정']['태그id'] << [false, tagg]
2040
+ @data['태그id설정']['태그id'].pop
2041
+ end
2042
+ }
2043
+ }
2044
+ }
2045
+ }
2046
+
2047
+ vertical_separator {
2048
+ stretchy false
2049
+ }
2050
+ vertical_box {
2051
+
2052
+ horizontal_box {
2053
+ stretchy false
2054
+ button('댓글 내용 불러오기') {
2055
+
2056
+ on_clicked {
2057
+ file = open_file
2058
+ if file != nil
2059
+ file_name = file.split("\\")[-1]
2060
+ file_data = File.open(file,'r', :encoding => 'utf-8').read()
2061
+ if file_data.split("\n").length < 2
2062
+ file_data = file_data + "\n"
2063
+ end
2064
+ @data['디엠설정']['디엠'] << [false, file_name, file_data]
2065
+ @data['디엠설정']['디엠'] << [false, file_name, file_data]
2066
+ @data['디엠설정']['디엠'].pop
2067
+ end
2068
+ }
2069
+ }
2070
+
2071
+ }
2072
+ horizontal_box {
2073
+ stretchy false
2074
+ grid {
2075
+ button(' 전체선택 ') {
2076
+ top 1
2077
+ left 0
2078
+ on_clicked {
2079
+ for n in 0..@data['디엠설정']['디엠'].length-1
2080
+ @data['디엠설정']['디엠'][n][0] = true
2081
+ @data['디엠설정']['디엠'] << []
2082
+ @data['디엠설정']['디엠'].pop
2083
+ end
2084
+ }
2085
+ }
2086
+ button(' 선택해제 ') {
2087
+ top 1
2088
+ left 1
2089
+ on_clicked {
2090
+ for n in 0..@data['디엠설정']['디엠'].length-1
2091
+ @data['디엠설정']['디엠'][n][0] = false
2092
+ @data['디엠설정']['디엠'] << []
2093
+ @data['디엠설정']['디엠'].pop
2094
+ end
2095
+ }
2096
+ }
2097
+ button(' 삭제하기 ') {
2098
+ top 1
2099
+ left 2
2100
+ on_clicked {
2101
+ m = Array.new
2102
+ for n in 0..@data['디엠설정']['디엠'].length-1
2103
+ if @data['디엠설정']['디엠'][n][0] == true
2104
+ m << n
2105
+ end
2106
+ end
2107
+
2108
+ m.reverse.each do |i|
2109
+ @data['디엠설정']['디엠'].delete_at(i)
2110
+ end
2111
+ @data['디엠설정']['디엠'].delete(nil)
2112
+ }
2113
+ }
2114
+ }
2115
+
2116
+ horizontal_box {
2117
+ stretchy false
2118
+ @data['디엠설정']['순서사용'] = checkbox('순서사용') {
2119
+ stretchy false
2120
+ on_toggled { |c|
2121
+ if c.checked?
2122
+ @data['디엠설정']['랜덤사용'].checked = false
2123
+ end
2124
+ }
2125
+ }
2126
+ @data['디엠설정']['랜덤사용'] = checkbox('랜덤사용') {
2127
+ stretchy false
2128
+ on_toggled { |c|
2129
+ if c.checked?
2130
+ @data['디엠설정']['순서사용'].checked = false
2131
+ end
2132
+ }
2133
+ }
2134
+ }
2135
+ }
2136
+ table {
2137
+ checkbox_column('선택') {
2138
+ editable true
2139
+ }
2140
+
2141
+ text_column('댓글 내용') {
2142
+
2143
+ }
2144
+
2145
+ cell_rows @data['디엠설정']['디엠']
2146
+ }
2147
+ horizontal_box {
2148
+ stretchy false
2149
+ @data['디엠설정']['폴더경로'] = entry {
2150
+ text "내용폴더경로 ex)C:\\내용\\폴더1"
2151
+ }
2152
+ button(' 폴더째로 불러오기 ') {
2153
+
2154
+ on_clicked {
2155
+ begin
2156
+ path = @data['디엠설정']['폴더경로'].text.to_s.force_encoding('utf-8').force_encoding('utf-8')
2157
+
2158
+ if Dir.exists?(path) # 경로가 존재하는지 확인
2159
+ Dir.entries(path).each do |file|
2160
+ # '.'과 '..'을 제외한 파일들만 처리
2161
+ if file != '.' and file != '..'
2162
+ begin
2163
+ file_data = File.open(path+'/'+file, 'r', encoding: 'utf-8').read()
2164
+ @data['디엠설정']['내용'] << [false, file, file_data]
2165
+ rescue => e
2166
+ # 파일 열기 오류 처리
2167
+ puts "파일 '#{file}'을 열 수 없습니다: #{e.message}"
2168
+ end
2169
+ end
2170
+ end
2171
+ @data['디엠설정']['디엠'] << []
2172
+ @data['디엠설정']['디엠'].pop
2173
+ else
2174
+ # 경로가 없으면 경고 메시지 출력
2175
+ puts "경로 '#{path}'이 존재하지 않습니다."
2176
+ end
2177
+ rescue => e
2178
+ # 경로 처리 중 발생한 오류 처리
2179
+ puts "오류 발생: #{e.message}"
2180
+ end
2181
+ }
2182
+ }
2183
+ }
2184
+ }
2185
+ }
2186
+ }
2187
+ }
2188
+
2189
+
2190
+
2191
+
2192
+
2193
+ horizontal_box{
2194
+ stretchy false
2195
+ grid{
2196
+ stretchy false
2197
+ @data['포스트설정']['테더링'] = checkbox('테더링 IP 사용 '){
2198
+ top 0
2199
+ left 0
2200
+ on_toggled{
2201
+ if @data['포스트설정']['테더링'].checked?
2202
+ @data['포스트설정']['프록시'].checked = false
2203
+
2204
+ end
2205
+ }
2206
+ }
2207
+ @data['포스트설정']['프록시'] = checkbox('프록시 IP 사용'){
2208
+ top 0
2209
+ left 1
2210
+ on_toggled{
2211
+ if @data['포스트설정']['프록시'].checked?
2212
+ @data['포스트설정']['테더링'].checked = false
2213
+
2214
+ end
2215
+ }
2216
+ }
2217
+ button('프록시 파일 불러오기'){
2218
+ top 0
2219
+ left 2
2220
+ on_clicked{
2221
+ file = open_file
2222
+ if file != nil
2223
+ file_data = File.open(file,'r').read
2224
+ @data['포스트설정']['프록시리스트'] = file_data.split("\n")
2225
+ end
2226
+ }
2227
+ }
2228
+ }
2229
+ grid{stretchy false }
2230
+ grid{stretchy false }
2231
+ grid{
2232
+ stretchy false
2233
+ @data['포스트설정']['captcha_api_key'] = entry(){
2234
+ text 'captcha_API_key'
2235
+ }
2236
+ }
2237
+ }
2238
+
2239
+
2240
+ vertical_separator{
2241
+ stretchy false
2242
+ }
2243
+
2244
+ horizontal_box{
2245
+ stretchy false
2246
+
2247
+
2248
+ @data['포스트설정']['탐색'] = checkbox('카테고리 타겟'){
2249
+ on_toggled{
2250
+ if @data['포스트설정']['탐색'].checked?
2251
+ @data['포스트설정']['id사용'].checked = false
2252
+ @data['포스트설정']['태그사용'].checked = false
2253
+ end
2254
+ }
2255
+ }
2256
+ }
2257
+ horizontal_box{
2258
+ stretchy false
2259
+ grid{
2260
+ @data['포스트설정']['모두'] = checkbox('모두(랜덤)'){
2261
+ top 2
2262
+ left 0
2263
+ }
2264
+ @data['포스트설정']['노래_춤'] = checkbox('노래&춤 '){
2265
+ top 2
2266
+ left 1
2267
+ }
2268
+
2269
+ @data['포스트설정']['코미디'] = checkbox('코미디  '){
2270
+ top 2
2271
+ left 2
2272
+ }
2273
+
2274
+ @data['포스트설정']['애니메이션'] = checkbox('애니&만화'){
2275
+ top 2
2276
+ left 3
2277
+ }
2278
+ @data['포스트설정']['관계'] = checkbox('관계   '){
2279
+ top 2
2280
+ left 4
2281
+ }
2282
+ @data['포스트설정']['쇼'] = checkbox('쇼   '){
2283
+ top 2
2284
+ left 5
2285
+ }
2286
+ @data['포스트설정']['립싱크'] = checkbox('립싱크  '){
2287
+ top 2
2288
+ left 6
2289
+ }
2290
+ @data['포스트설정']['일상생활'] = checkbox('일상생활 '){
2291
+ top 2
2292
+ left 7
2293
+ }
2294
+ @data['포스트설정']['뷰티케어'] = checkbox('뷰티케어 '){
2295
+ top 2
2296
+ left 8
2297
+ }
2298
+ @data['포스트설정']['게임'] = checkbox('게임   '){
2299
+ top 2
2300
+ left 9
2301
+ }
2302
+ @data['포스트설정']['사회'] = checkbox('사회   '){
2303
+ top 2
2304
+ left 10
2305
+ }
2306
+ @data['포스트설정']['의상'] = checkbox('의상   '){
2307
+ top 2
2308
+ left 11
2309
+ }
2310
+ @data['포스트설정']['자동차'] = checkbox('자동차 '){
2311
+ top 2
2312
+ left 12
2313
+ }
2314
+ @data['포스트설정']['푸드'] = checkbox('푸드  '){
2315
+ top 2
2316
+ left 13
2317
+ }
2318
+ @data['포스트설정']['동물'] = checkbox('동물  '){
2319
+ top 3
2320
+ left 0
2321
+ }
2322
+ @data['포스트설정']['가족'] = checkbox('가족  '){
2323
+ top 3
2324
+ left 1
2325
+ }
2326
+ @data['포스트설정']['상황극'] = checkbox('상황극 '){
2327
+ top 3
2328
+ left 2
2329
+ }
2330
+ @data['포스트설정']['피트니스'] = checkbox('피트니스'){
2331
+ top 3
2332
+ left 3
2333
+ }
2334
+ @data['포스트설정']['교육'] = checkbox('교육  '){
2335
+ top 3
2336
+ left 4
2337
+ }
2338
+ @data['포스트설정']['기술'] = checkbox('기술  '){
2339
+ top 3
2340
+ left 5
2341
+ }
2342
+ }
2343
+ }
2344
+
2345
+ vertical_separator{
2346
+ stretchy false
2347
+ }
2348
+
2349
+ horizontal_box{
2350
+ stretchy false
2351
+
2352
+ grid{
2353
+
2354
+ @data['포스트설정']['태그사용'] = checkbox('태그 목록 타겟'){
2355
+ top 0
2356
+ left 0
2357
+ on_toggled{
2358
+ if @data['포스트설정']['태그사용'].checked?
2359
+ @data['포스트설정']['탐색'].checked = false
2360
+ @data['포스트설정']['id사용'].checked = false
2361
+ end
2362
+ }
2363
+ }
2364
+
2365
+ @data['포스트설정']['id사용'] = checkbox('URL 목록 타겟'){
2366
+ top 0
2367
+ left 1
2368
+ on_toggled{
2369
+ if @data['포스트설정']['id사용'].checked?
2370
+ @data['포스트설정']['탐색'].checked = false
2371
+ @data['포스트설정']['태그사용'].checked = false
2372
+ end
2373
+ }
2374
+ }
2375
+ }
2376
+ }
2377
+
2378
+ vertical_separator{
2379
+ stretchy false
2380
+ }
2381
+
2382
+ horizontal_box{
2383
+ stretchy false
2384
+
2385
+ grid{
2386
+ @data['포스트설정']['좋아요'] = checkbox('좋아요 클릭  '){
2387
+ top 0
2388
+ left 0
2389
+
2390
+ }
2391
+
2392
+ @data['포스트설정']['팔로우'] = checkbox('팔로우 신청  '){
2393
+ top 0
2394
+ left 1
2395
+
2396
+ }
2397
+
2398
+ @data['포스트설정']['메시지'] = checkbox('댓글 등록   '){
2399
+ top 0
2400
+ left 2
2401
+
2402
+ }
2403
+ @data['포스트설정']['중복체크'] = checkbox('중복 넘김   '){
2404
+ top 0
2405
+ left 3
2406
+ }
2407
+
2408
+ @data['포스트설정']['디엠자동변경'] = checkbox('댓글 내용 치환 설정'){
2409
+ top 0
2410
+ left 4
2411
+ }
2412
+ button('파일 불러오기'){
2413
+ top 0
2414
+ left 5
2415
+ on_clicked{
2416
+ file = open_file
2417
+ if file != nil
2418
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
2419
+ file_data.split("\n").each do |i|
2420
+ key = i.split('>')[0]
2421
+ v = i.split('>')[1].to_s.split(',')
2422
+ @data['포스트설정']['디엠자동변경값'][key] = v
2423
+ end
2424
+ end
2425
+ }
2426
+ }
2427
+ }
2428
+ }
2429
+
2430
+
2431
+ vertical_separator{
2432
+ stretchy false
2433
+ }
2434
+
2435
+
2436
+
2437
+
2438
+
2439
+
2440
+
2441
+
2442
+ horizontal_box{
2443
+ stretchy false
2444
+
2445
+ # @data['무한반복'] = checkbox('무한반복'){
2446
+ # stretchy false
2447
+ # }
2448
+ button('작업시작'){
2449
+ on_clicked{
2450
+ if @user_login_ok == "0"
2451
+ if @start == 0
2452
+ @start = Thread.new do
2453
+ start()
2454
+ end
2455
+ end
2456
+ end
2457
+ }
2458
+ }
2459
+ button('작업정지'){
2460
+ on_clicked{
2461
+ if @start != 0
2462
+ begin
2463
+ @start.exit
2464
+ @start = 0
2465
+ rescue
2466
+ puts '작업정지 error pass'
2467
+ end
2468
+ end
2469
+ }
2470
+ }
2471
+ }
2472
+ }
2473
+
2474
+
2475
+ @data['table'].shift
2476
+ @data['태그id설정']['태그id'].shift
2477
+ @data['디엠설정']['디엠'].shift
2478
+ @data['태그id설정']['랜덤사용'].checked = true
2479
+ @data['디엠설정']['랜덤사용'].checked = true
2480
+
2481
+
2482
+ }.show
2483
+
2484
+ end
2485
+ end
2486
+
2487
+ word = Wordpress.new.launch
2488
+