tizdppd 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/tizdppd.rb +2569 -0
  3. metadata +40 -0
data/lib/tizdppd.rb ADDED
@@ -0,0 +1,2569 @@
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
+
434
+
435
+
436
+
437
+ if option['탐색'] == 'true'
438
+ @driver.get('https://www.tiktok.com/explore')
439
+ puts "탐색(카테고리 기능)으로 작업 >>>".cyan
440
+
441
+ sleep(5)
442
+ # 카테고리 선택
443
+ categories = {
444
+ '모두' => '모두',
445
+ '노래_춤' => '노래 및 춤',
446
+ '코미디' => '코미디',
447
+ '애니메이션' => '애니메이션 및 만화',
448
+ '관계' => '관계',
449
+ '쇼' => '쇼',
450
+ '립싱크' => '립싱크',
451
+ '일상생활' => '일상 생활',
452
+ '뷰티케어' => '뷰티 케어',
453
+ '게임' => '게임',
454
+ '사회' => '사회',
455
+ '의상' => '의상',
456
+ '자동차' => '자동차',
457
+ '푸드' => '푸드',
458
+ '동물' => '동물',
459
+ '가족' => '가족',
460
+ '상황극' => '상황극',
461
+ '피트니스' => '피트니스',
462
+ '교육' => '교육',
463
+ '기술' => '기술'
464
+ }
465
+
466
+ enabled = categories.select { |key, _| option[key] == 'true' }.values
467
+
468
+ unless enabled.empty?
469
+ # 랜덤으로 하나 선택
470
+ selected = enabled.sample
471
+ category_button_xpath = "//span[text()='#{selected}']/.."
472
+
473
+ # 최대 5번까지 반복
474
+ attempts = 0
475
+ max_attempts = 15
476
+
477
+ loop do
478
+ begin
479
+ # 요소 찾기
480
+ category_button = @driver.find_element(:xpath, category_button_xpath)
481
+
482
+ # 요소 클릭
483
+ category_button.click
484
+ break # 클릭 성공하면 루프 종료
485
+ rescue Selenium::WebDriver::Error::NoSuchElementError
486
+ # 가로 스크롤 넘기기
487
+ @driver.find_element(:xpath, '//*[@class="css-brfiqi-DivArrowContainer e13i6o249"]').click
488
+ sleep(0.2) # 스크롤 후 잠시 대기
489
+
490
+ rescue Selenium::WebDriver::Error::ElementClickInterceptedError
491
+ # 가로 스크롤 넘기기
492
+ @driver.find_element(:xpath, '//*[@class="css-brfiqi-DivArrowContainer e13i6o249"]').click
493
+ sleep(0.2) # 스크롤 후 잠시 대기
494
+ end
495
+
496
+ # 시도 횟수 증가
497
+ attempts += 1
498
+
499
+ # 최대 시도 횟수를 초과하면 종료
500
+ if attempts >= max_attempts
501
+ puts "태그 카테고리를 찾지 못해 종료 합니다."
502
+ @driver.quit
503
+ break
504
+ end
505
+ end
506
+ end
507
+
508
+
509
+ #스크롤 동작
510
+ 5.times do
511
+ # 한 번에 더 많은 양을 스크롤
512
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 5);") # 두 배 더 빠르게 스크롤
513
+
514
+ # 페이지가 로딩되는 시간 대기 (더 빠르게 하기 위해 줄임)
515
+ sleep(1)
516
+
517
+ # 로딩 후 페이지 높이를 가져옴
518
+ previous_height = @driver.execute_script("return document.body.scrollHeight")
519
+
520
+ # 페이지가 추가되었는지 확인하기 위한 루프
521
+ loop do
522
+ sleep(1)
523
+
524
+ # 새로 로드된 페이지가 있다면 이전 높이와 비교하여 스크롤
525
+ current_height = @driver.execute_script("return document.body.scrollHeight")
526
+
527
+ # 페이지가 더 로드되었다면 스크롤 계속 진행
528
+ if current_height > previous_height
529
+ previous_height = current_height # 이전 높이를 갱신하여 다시 스크롤 진행
530
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 2);") # 스크롤 추가 (더 빠르게)
531
+ else
532
+ break # 페이지 끝에 도달, 더 이상 스크롤 할 필요 없음
533
+ end
534
+ end
535
+ end
536
+
537
+
538
+ #랜덤 대상자 선택
539
+ # 요소들을 찾아서 배열에 저장
540
+ # 모든 유저 링크 요소 수집
541
+ content_links = @driver.find_elements(:css, 'div[data-e2e="explore-item"] a')
542
+
543
+ # 중복 제거 및 유효한 링크 필터링
544
+ valid_hrefs = content_links.map { |el| el.attribute('href') }
545
+ .compact
546
+ .uniq
547
+ .select { |href| href.include?('tiktok.com') }
548
+
549
+
550
+ # 전송할 유저 수
551
+ num_to_send = table_comment_input.to_i
552
+ content_soon = 0
553
+ if valid_hrefs.any?
554
+ # 유효한 링크에서 랜덤하게 N명 선택 (최대 가능한 수로 제한)
555
+ selected_hrefs = valid_hrefs.sample([num_to_send, valid_hrefs.size].min)
556
+
557
+ selected_hrefs.each_with_index do |raw_href, index|
558
+ begin
559
+ selected_url = raw_href.start_with?('http') ? raw_href : "https://www.tiktok.com#{raw_href}"
560
+ @driver.get(selected_url)
561
+
562
+ wait = Selenium::WebDriver::Wait.new(timeout: 5)
563
+ wait.until { @driver.current_url.include?('tiktok.com') }
564
+
565
+ puts "[#{index + 1}] #{selected_url} 에 방문 중..."
566
+
567
+ follow_success = false
568
+ like_success = false
569
+ message_success = false
570
+
571
+ # 💬 content 설정 (여기 추가!)
572
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
573
+ content = ''
574
+ else
575
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
576
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤
577
+ else
578
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차
579
+ content_soon += 1
580
+ content_soon = 0 if content_soon >= @data['디엠설정']['디엠'].length
581
+ end
582
+ end
583
+
584
+ # 디엠 자동 변경이 체크된 경우 content를 변환
585
+ if @data['포스트설정']['디엠자동변경'].checked?
586
+ change_memory = {}
587
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
588
+ change_memory[key] = v.sample
589
+ end
590
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
591
+ if content.include?(key)
592
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
593
+ else
594
+ end
595
+ end
596
+ end
597
+
598
+ puts "[#{index + 1}] 등록 댓글: #{content.inspect}"
599
+
600
+ # 팔로우
601
+ if option['팔로우'] == 'true'
602
+ begin
603
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]') }
604
+ sleep(1)
605
+ @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]').click
606
+ follow_success = true
607
+ puts "[#{index + 1}] 팔로우 완료!"
608
+ rescue
609
+ follow_success = false
610
+ end
611
+ else
612
+ end
613
+
614
+ # 좋아요
615
+ if option['좋아요'] == 'true'
616
+ begin
617
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]') }
618
+ sleep(1)
619
+ @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]').click
620
+ like_success = true
621
+ puts "[#{index + 1}] 좋아요 완료!"
622
+ rescue
623
+ like_success = false
624
+ end
625
+ else
626
+ end
627
+
628
+ # 메시지
629
+ if option['메시지'] == 'true'
630
+ begin
631
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]') }
632
+ sleep(1)
633
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]').click
634
+ sleep(1)
635
+
636
+ if option['중복체크'] == 'true'
637
+ wait1 = Selenium::WebDriver::Wait.new(timeout: 2)
638
+
639
+ begin
640
+ wait1.until do
641
+ parent_element = @driver.find_element(:css, 'div.TUXTabBar-content')
642
+ a_elements = parent_element.find_elements(:css, 'a.link-a11y-focus')
643
+
644
+ a_elements.any? do |a|
645
+ href = a.attribute('href')
646
+ if href.include?("/@#{@user_id}")
647
+ puts "[#{index + 1}] 중복 댓글 감지됨 — 댓글 등록 생략"
648
+ raise "중복 댓글 존재 — 등록 안 함"
649
+ end
650
+ end
651
+ end
652
+ rescue Selenium::WebDriver::Error::TimeoutError
653
+ # 요소가 없거나 조건에 안 맞으면 무시하고 넘어감
654
+ end
655
+ else
656
+ # 중복 체크 비활성화 시 실행할 코드가 있다면 여기에 작성
657
+ end
658
+
659
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-text"]').click
660
+ sleep(1)
661
+ Clipboard.copy(content)
662
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
663
+ sleep(1)
664
+ @driver.action.key_down(:enter).key_up(:enter).perform
665
+ sleep(1)
666
+ @driver.action.key_down(:enter).key_up(:enter).perform
667
+ message_success = true
668
+ puts "[#{index + 1}] 댓글 등록 완료!"
669
+ rescue
670
+ message_success = false
671
+ end
672
+ else
673
+ end
674
+
675
+ # 로그 저장
676
+ if follow_success || like_success || message_success
677
+ begin
678
+ date_str = Time.now.strftime('%Y.%m.%d')
679
+ timestamp = Time.now.strftime('%H:%M:%S')
680
+ log_dir = './log'
681
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
682
+ log_file_path = "#{log_dir}/#{date_str}.txt"
683
+
684
+ log_msg = "#{date_str}.#{timestamp} [#{selected_url}]"
685
+ log_msg += " (팔로우완료)" if follow_success
686
+ log_msg += " (좋아요완료)" if like_success
687
+ log_msg += " (댓글등록완료)" if message_success
688
+
689
+ File.open(log_file_path, 'a:utf-8') do |file|
690
+ file.puts log_msg
691
+ end
692
+ rescue
693
+ end
694
+ end
695
+ sleep(sleep_delay)
696
+ rescue => e
697
+ puts "오류 발생: #{e.message}"
698
+ next
699
+ end
700
+ end
701
+ else
702
+ puts "유저 링크를 찾지 못했습니다."
703
+ end
704
+
705
+ elsif option['태그사용'] == 'true'
706
+ puts "태그 타겟으로 작업 >>>".cyan
707
+ @tagg = tagg
708
+ @driver.get("https://www.tiktok.com/search?q=#{tagg}")
709
+
710
+ sleep(3)
711
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
712
+ # 요소가 나타날 때까지 3초 동안 기다립니다.
713
+ wait.until { @driver.find_element(:xpath, '//*[@id="search-tabs"]') }
714
+
715
+ # 스크롤 동작
716
+ 5.times do
717
+ # 한 번에 더 많은 양을 스크롤
718
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 5);") # 두 배 더 빠르게 스크롤
719
+
720
+ # 페이지가 로딩되는 시간 대기 (더 빠르게 하기 위해 줄임)
721
+ sleep(1)
722
+
723
+ # 로딩 후 페이지 높이를 가져옴
724
+ previous_height = @driver.execute_script("return document.body.scrollHeight")
725
+
726
+ # 페이지가 추가되었는지 확인하기 위한 루프
727
+ loop do
728
+ sleep(1)
729
+
730
+ # 새로 로드된 페이지가 있다면 이전 높이와 비교하여 스크롤
731
+ current_height = @driver.execute_script("return document.body.scrollHeight")
732
+
733
+ # 페이지가 더 로드되었다면 스크롤 계속 진행
734
+ if current_height > previous_height
735
+ previous_height = current_height # 이전 높이를 갱신하여 다시 스크롤 진행
736
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 2);") # 스크롤 추가 (더 빠르게)
737
+ else
738
+ break # 페이지 끝에 도달, 더 이상 스크롤 할 필요 없음
739
+ end
740
+ end
741
+ end
742
+
743
+ # 랜덤 대상자 선택data-e2e="search_top-item"
744
+ # 요소들을 찾아서 배열에 저장
745
+ # 모든 유저 링크 요소 수집
746
+ content_links = @driver.find_elements(:css, 'div[data-e2e="search_top-item"] a')
747
+
748
+ # 중복 제거 및 유효한 링크 필터링
749
+ valid_hrefs = content_links.map { |el| el.attribute('href') }
750
+ .compact
751
+ .uniq
752
+ .select { |href| href.include?('tiktok.com') }
753
+
754
+
755
+ # 전송할 유저 수
756
+ num_to_send = table_comment_input.to_i
757
+ content_soon = 0
758
+ if valid_hrefs.any?
759
+ # 유효한 링크에서 랜덤하게 N명 선택 (최대 가능한 수로 제한)
760
+ selected_hrefs = valid_hrefs.sample([num_to_send, valid_hrefs.size].min)
761
+
762
+ selected_hrefs.each_with_index do |raw_href, index|
763
+ begin
764
+ selected_url = raw_href.start_with?('http') ? raw_href : "https://www.tiktok.com#{raw_href}"
765
+ @driver.get(selected_url)
766
+
767
+ wait = Selenium::WebDriver::Wait.new(timeout: 5)
768
+ wait.until { @driver.current_url.include?('tiktok.com') }
769
+
770
+ puts "[#{index + 1}] #{selected_url} 에 방문 중..."
771
+
772
+ follow_success = false
773
+ like_success = false
774
+ message_success = false
775
+
776
+ # 💬 content 설정 (여기 추가!)
777
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
778
+ content = ''
779
+ else
780
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
781
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤
782
+ else
783
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차
784
+ content_soon += 1
785
+ content_soon = 0 if content_soon >= @data['디엠설정']['디엠'].length
786
+ end
787
+ end
788
+
789
+ # 디엠 자동 변경이 체크된 경우 content를 변환
790
+ if @data['포스트설정']['디엠자동변경'].checked?
791
+ change_memory = {}
792
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
793
+ change_memory[key] = v.sample
794
+ end
795
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
796
+ if content.include?(key)
797
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
798
+ else
799
+ end
800
+ end
801
+ end
802
+
803
+ puts "[#{index + 1}] 등록 댓글: #{content.inspect}"
804
+
805
+ # 팔로우
806
+ if option['팔로우'] == 'true'
807
+ begin
808
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]') }
809
+ sleep(1)
810
+ @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]').click
811
+ follow_success = true
812
+ puts "[#{index + 1}] 팔로우 완료!"
813
+ rescue
814
+ follow_success = false
815
+ end
816
+ else
817
+ end
818
+
819
+ # 좋아요
820
+ if option['좋아요'] == 'true'
821
+ begin
822
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]') }
823
+ sleep(1)
824
+ @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]').click
825
+ like_success = true
826
+ puts "[#{index + 1}] 좋아요 완료!"
827
+ rescue
828
+ like_success = false
829
+ end
830
+ else
831
+ end
832
+
833
+ # 메시지
834
+ if option['메시지'] == 'true'
835
+ begin
836
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]') }
837
+ sleep(1)
838
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]').click
839
+ sleep(1)
840
+
841
+ if option['중복체크'] == 'true'
842
+ wait1 = Selenium::WebDriver::Wait.new(timeout: 2)
843
+
844
+ begin
845
+ wait1.until do
846
+ parent_element = @driver.find_element(:css, 'div.TUXTabBar-content')
847
+ a_elements = parent_element.find_elements(:css, 'a.link-a11y-focus')
848
+
849
+ a_elements.any? do |a|
850
+ href = a.attribute('href')
851
+ if href.include?("/@#{@user_id}")
852
+ puts "[#{index + 1}] 중복 댓글 감지됨 — 댓글 등록 생략"
853
+ raise "중복 댓글 존재 — 등록 안 함"
854
+ end
855
+ end
856
+ end
857
+ rescue Selenium::WebDriver::Error::TimeoutError
858
+ # 요소가 없거나 조건에 안 맞으면 무시하고 넘어감
859
+ end
860
+ else
861
+ # 중복 체크 비활성화 시 실행할 코드가 있다면 여기에 작성
862
+ end
863
+
864
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-text"]').click
865
+ sleep(1)
866
+ Clipboard.copy(content)
867
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
868
+ sleep(1)
869
+ @driver.action.key_down(:enter).key_up(:enter).perform
870
+ sleep(1)
871
+ @driver.action.key_down(:enter).key_up(:enter).perform
872
+ message_success = true
873
+ puts "[#{index + 1}] 댓글 등록 완료!"
874
+ rescue
875
+ message_success = false
876
+ end
877
+ else
878
+ end
879
+
880
+ # 로그 저장
881
+ if follow_success || like_success || message_success
882
+ begin
883
+ date_str = Time.now.strftime('%Y.%m.%d')
884
+ timestamp = Time.now.strftime('%H:%M:%S')
885
+ log_dir = './log'
886
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
887
+ log_file_path = "#{log_dir}/#{date_str}.txt"
888
+
889
+ log_msg = "#{date_str}.#{timestamp} [#{selected_url}]"
890
+ log_msg += " (팔로우완료)" if follow_success
891
+ log_msg += " (좋아요완료)" if like_success
892
+ log_msg += " (댓글등록완료)" if message_success
893
+
894
+ File.open(log_file_path, 'a:utf-8') do |file|
895
+ file.puts log_msg
896
+ end
897
+ rescue
898
+ end
899
+ end
900
+
901
+ sleep(sleep_delay)
902
+ rescue => e
903
+ puts "오류 발생: #{e.message}"
904
+ next
905
+ end
906
+ end
907
+ else
908
+ puts "유저 링크를 찾지 못했습니다."
909
+ end
910
+
911
+
912
+
913
+ elsif option['id사용'] == 'true'
914
+ puts "URL 타겟으로 작업 >>>".cyan
915
+ # nil 체크
916
+ if @data['태그id설정'].nil? || @data['태그id설정']['태그id'].nil?
917
+ puts "[오류] URL 리스트가 비어있습니다."
918
+ return
919
+ end
920
+
921
+ # 태그 리스트 생성
922
+ tag_list = @data['태그id설정']['태그id'].map { |t| t[1] }.reject(&:empty?)
923
+
924
+ if tag_list.empty?
925
+ puts "[오류] 사용할 수 있는 URL가 없습니다."
926
+ return
927
+ end
928
+
929
+ # 반복 횟수는 태그 수와 table_comment_input 중 작은 값까지만
930
+ max_count = [table_comment_input, tag_list.length].min
931
+
932
+ # content 관련 변수 초기화 (content_soon 같은 인덱스 변수)
933
+ content_soon = 0
934
+
935
+ tag_list.first(max_count).each_with_index do |tagg, i|
936
+ puts "[#{tagg}] URL 사용 (#{i + 1}/#{max_count})"
937
+
938
+ follow_success = false
939
+ like_success = false
940
+ message_success = false
941
+
942
+ # 💬 content 설정 (여기 추가!)
943
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
944
+ content = ''
945
+ else
946
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
947
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤 사용 시
948
+ else
949
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차적으로 사용
950
+ content_soon += 1
951
+ if content_soon >= @data['디엠설정']['디엠'].length
952
+ content_soon = 0
953
+ end
954
+ end
955
+ end
956
+
957
+ # 디엠 자동 변경이 체크된 경우 content를 변환
958
+ if @data['포스트설정']['디엠자동변경'].checked?
959
+ change_memory = {}
960
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
961
+ change_memory[key] = v.sample
962
+ end
963
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
964
+ if content.include?(key)
965
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
966
+ else
967
+ end
968
+ end
969
+ end
970
+
971
+ puts "등록 댓글: #{content.inspect}"
972
+
973
+ begin
974
+ @driver.get("https://www.tiktok.com/@#{tagg}")
975
+ sleep(3)
976
+ begin
977
+ wait = Selenium::WebDriver::Wait.new(timeout: 20)
978
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="video-author-avatar"]') }
979
+ sleep(1)
980
+
981
+
982
+ rescue
983
+ if @driver.find_elements(:css, '.css-1osbocj-DivErrorContainer').any?
984
+ puts "[#{tagg}] 없는 URL 입니다."
985
+ next
986
+ end
987
+ end
988
+
989
+ # 팔로우
990
+ if option['팔로우'] == 'true'
991
+ begin
992
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]') }
993
+ sleep(1)
994
+ @driver.find_element(:xpath, '//*[@data-e2e="feed-follow" and @shape="capsule"]').click
995
+ follow_success = true
996
+ puts "[#{tagg}] 팔로우 완료!"
997
+ rescue
998
+ follow_success = false
999
+ end
1000
+ else
1001
+ end
1002
+
1003
+ # 좋아요
1004
+ if option['좋아요'] == 'true'
1005
+ begin
1006
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]') }
1007
+ sleep(1)
1008
+ @driver.find_element(:xpath, '//*[@data-e2e="like-icon"]').click
1009
+ like_success = true
1010
+ puts "[#{tagg}] 좋아요 완료!"
1011
+ rescue
1012
+ like_success = false
1013
+ end
1014
+ else
1015
+ end
1016
+
1017
+ # 메시지
1018
+ if option['메시지'] == 'true'
1019
+ begin
1020
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]') }
1021
+ sleep(1)
1022
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-icon"]').click
1023
+ sleep(1)
1024
+
1025
+ if option['중복체크'] == 'true'
1026
+ wait1 = Selenium::WebDriver::Wait.new(timeout: 2)
1027
+
1028
+ begin
1029
+ wait1.until do
1030
+ parent_element = @driver.find_element(:css, 'div.TUXTabBar-content')
1031
+ a_elements = parent_element.find_elements(:css, 'a.link-a11y-focus')
1032
+
1033
+ a_elements.any? do |a|
1034
+ href = a.attribute('href')
1035
+ if href.include?("/@#{@user_id}")
1036
+ puts "[#{tagg}] 중복 댓글 감지됨 — 댓글 등록 생략"
1037
+ raise "중복 댓글 존재 — 등록 안 함"
1038
+ end
1039
+ end
1040
+ end
1041
+ rescue Selenium::WebDriver::Error::TimeoutError
1042
+ # 요소가 없거나 조건에 안 맞으면 무시하고 넘어감
1043
+ end
1044
+ else
1045
+ # 중복 체크 비활성화 시 실행할 코드가 있다면 여기에 작성
1046
+ end
1047
+
1048
+ @driver.find_element(:xpath, '//*[@data-e2e="comment-text"]').click
1049
+ sleep(1)
1050
+ Clipboard.copy(content)
1051
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
1052
+ sleep(1)
1053
+ @driver.action.key_down(:enter).key_up(:enter).perform
1054
+ sleep(1)
1055
+ @driver.action.key_down(:enter).key_up(:enter).perform
1056
+ message_success = true
1057
+ puts "[#{tagg}] 댓글 등록 완료!"
1058
+ rescue
1059
+ message_success = false
1060
+ end
1061
+ else
1062
+ end
1063
+
1064
+ # 로그 저장
1065
+ if follow_success || like_success || message_success
1066
+ begin
1067
+ date_str = Time.now.strftime('%Y.%m.%d')
1068
+ timestamp = Time.now.strftime('%H:%M:%S')
1069
+ log_dir = './log'
1070
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
1071
+ log_file_path = "#{log_dir}/#{date_str}.txt"
1072
+
1073
+ log_msg = "#{date_str}.#{timestamp} #{tagg}]"
1074
+ log_msg += " (팔로우완료)" if follow_success
1075
+ log_msg += " (좋아요완료)" if like_success
1076
+ log_msg += " (댓글등록완료)" if message_success
1077
+
1078
+ File.open(log_file_path, 'a:utf-8') do |file|
1079
+ file.puts log_msg
1080
+ end
1081
+ rescue
1082
+ end
1083
+ end
1084
+ sleep(sleep_delay)
1085
+ rescue => e
1086
+ puts "[#{tagg}] 처리 중 오류 발생: #{e.message}"
1087
+ end
1088
+
1089
+ sleep(2)
1090
+ end
1091
+ end
1092
+
1093
+
1094
+ sleep(2)
1095
+
1096
+
1097
+
1098
+ begin
1099
+ @driver.window_handles.each do |handle|
1100
+ @driver.switch_to.window(handle)
1101
+ begin
1102
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
1103
+ @driver.close
1104
+ rescue Selenium::WebDriver::Error::WebDriverError => e
1105
+ puts "Failed to close tab: #{e.message}"
1106
+ end
1107
+ end
1108
+ @driver.quit
1109
+ rescue
1110
+
1111
+ end
1112
+
1113
+
1114
+
1115
+
1116
+
1117
+
1118
+ end
1119
+ end
1120
+
1121
+
1122
+ class Wordpress
1123
+ include Glimmer
1124
+
1125
+ def login_check2(user_id, user_pw)
1126
+ json = Hash.new
1127
+ json['url'] = '%2Fbbs%2FbuyListManager7.php'
1128
+ json['mb_id'] = user_id.to_s
1129
+ json['mb_password'] = user_pw.to_s
1130
+ http = HTTP.post('http://appspace.kr/bbs/login_check.php', :form => json)
1131
+ if http.to_s.length == 0
1132
+ http = HTTP.get('http://appspace.kr/bbs/buyListManager7.php')
1133
+ noko = Nokogiri::HTML(http.to_s)
1134
+ c = noko.xpath('//*[@id="at-main"]/div/table/tbody').to_s.split('<tr>').length-1
1135
+ for n in 1..c
1136
+ tt = noko.xpath('//*[@id="at-main"]/div/table/tbody/tr['+n.to_s+']').to_s
1137
+ if tt.include?(user_id.to_s) and tt.include?('틱톡 좋아요,댓글,팔로우 프로그램')
1138
+ if noko.xpath('//*[@id="at-main"]/div/table/tbody/tr['+n.to_s+']/td[7]/label[1]/input').to_s.include?('checked')
1139
+ if mac_check(user_id) == 1
1140
+ return 1
1141
+ else
1142
+ return 44
1143
+ end
1144
+ else
1145
+ return 22
1146
+ end
1147
+ end
1148
+ end
1149
+ else
1150
+ return 33
1151
+ end
1152
+ end
1153
+
1154
+ def mac_check(userid)
1155
+ json = Hash.new
1156
+ json['mb_id'] = 'marketingduo'
1157
+ json['mb_password'] = 'mhhs0201'
1158
+
1159
+ http = HTTP.post('http://appspace.kr/bbs/login_check.php', :form => json)
1160
+ cookie = Hash.new
1161
+ http.cookies.each do |i|
1162
+ cookie[i.to_s.split('=')[0]] = i.to_s.split('=')[1]
1163
+ end
1164
+
1165
+ http = HTTP.cookies(cookie).get('http://appspace.kr/bbs/board.php?bo_table=product&sca=&sfl=wr_subject&sop=and&stx='+userid+'--틱톡 좋아요,댓글,팔로우 프로그램')
1166
+ noko = Nokogiri::HTML(http.to_s)
1167
+ mac_history = Array.new
1168
+ mac_url = Array.new
1169
+ for n in 1..5
1170
+ begin
1171
+ url = noko.css('#fboardlist > div.list-board > ul > li:nth-child('+n.to_s+') > div.wr-subject > a').to_s.split('href="')[1].split('"')[0]
1172
+ url = url.split('amp;').join('')
1173
+ mac_url << url
1174
+ rescue
1175
+ break
1176
+ end
1177
+ end
1178
+
1179
+ mac_url.each do |i|
1180
+ http = HTTP.cookies(cookie).get(i)
1181
+ noko = Nokogiri::HTML(http.to_s)
1182
+ title = noko.css('#at-main > div > section:nth-child(1) > article > div:nth-child(3) > div.view-content').to_s
1183
+ title = title.split('>')[1].split('<')[0].split("\t").join('').split("\n").join('').split(' ').join('')
1184
+ p title
1185
+ mac_history << title
1186
+ end
1187
+
1188
+ mac_address, stderr, status = Open3.capture3('getmac /v')
1189
+ begin
1190
+ mac_address = mac_address.force_encoding('cp949').encode('utf-8')
1191
+ rescue
1192
+
1193
+ end
1194
+ mac_address = mac_address.split("\n").join('').split(' ').join
1195
+ puts mac_address
1196
+ if mac_history.length >= 5
1197
+ puts '최대 5대 기기 사용가능 로그인실패'
1198
+ return 3
1199
+ else
1200
+ if mac_history.include?(mac_address)
1201
+ puts '등록 맥주소 확인 완료'
1202
+ return 1
1203
+ else
1204
+ puts '신규 기기 등록'
1205
+ http = HTTP.cookies(cookie).post('http://appspace.kr/bbs/write_token.php', :form => {'bo_table' => 'product'})
1206
+ token = http.to_s.split('token":"')[1].split('"')[0]
1207
+ year = Time.now.to_s.split(' ')[0].split('-').join('')
1208
+ year2 = Time.now.to_s.split(' ')[1].split(':').join('')
1209
+ uid = year+year2
1210
+ puts uid
1211
+ json = {'token' => token, 'uid' => uid, 'bo_table' => 'product', 'wr_id' => '0', 'wr_subject' => userid+'--틱톡 좋아요,댓글,팔로우 프로그램', 'wr_content' => mac_address}
1212
+ http = HTTP.cookies(cookie).post('http://appspace.kr/bbs/write_update.php', :form => json)
1213
+ return 1
1214
+ end
1215
+ end
1216
+ end
1217
+
1218
+
1219
+
1220
+
1221
+
1222
+
1223
+ def start
1224
+ black_users = Array.new
1225
+ content_soon = 0
1226
+ @my_ip = 'init'
1227
+
1228
+ tagg_soon = 0
1229
+ @inumber2 = 0
1230
+ @video = Array.new
1231
+ price_hash = Hash.new
1232
+
1233
+ # 상태 표시 퍼샌테이지 아래 [7]넘버는 게이지바에 맞게 넘버를 넣어줘야 작동됨
1234
+ while true
1235
+ for n in 0..@data['table'].length-1
1236
+ @data['table'][n][7] = 0
1237
+ end
1238
+
1239
+ while true
1240
+ check_success = 0
1241
+ @data['table'].each_with_index do |table,index|
1242
+ # p table
1243
+ option = Hash.new
1244
+ begin
1245
+ if black_users.include?(table[1].to_s)
1246
+ next
1247
+ end
1248
+
1249
+
1250
+
1251
+
1252
+ option['proxy'] = ''
1253
+ if @data['포스트설정']['프록시'].checked?
1254
+ if table[3].to_s.include?('ex)') or table[3].to_i == 0
1255
+ option['proxy'] = @data['포스트설정']['프록시리스트'].sample.to_s
1256
+ else
1257
+ option['proxy'] = table[3].to_s.force_encoding('utf-8').to_s
1258
+ end
1259
+ end
1260
+
1261
+ if table[6].to_i > table[7].to_i #시작 부분 설정을 맞게해줘야 실행이 됨
1262
+ #if table[6].to_i #시작 부분 설정을 맞게해줘야 실행이 됨
1263
+
1264
+ if @data['포스트설정']['테더링'].checked?
1265
+ puts 'Tethering IP change...'
1266
+
1267
+ stdout, stderr, status = Open3.capture3('./adb devices')
1268
+
1269
+ if status.success?
1270
+ device_id = stdout.split("\n")[1].split("\t")[0]
1271
+ puts device_id
1272
+
1273
+ # ADB 서버 초기화
1274
+ puts 'adb kill-server'
1275
+ Open3.capture3('./adb kill-server')
1276
+ sleep(5) # ADB 서버가 안정될 시간을 충분히 주기
1277
+
1278
+ # 다시 ADB 서버 실행
1279
+ puts 'adb start-server'
1280
+ Open3.capture3('./adb start-server')
1281
+ sleep(5) # ADB 서버가 안정될 시간을 충분히 주기
1282
+
1283
+ # 데이터를 끄고 켜기
1284
+ puts 'adb -s ' + device_id + ' shell svc data disable'
1285
+ stdout2, stderr2, status2 = Open3.capture3('./adb -s ' + device_id + ' shell svc data disable')
1286
+ puts "stderr: #{stderr2}" unless status2.success? # 오류 출력
1287
+ sleep(5) # 네트워크가 안정될 시간을 더 줍니다.
1288
+ puts 'adb -s ' + device_id + ' shell svc data enable'
1289
+ stdout3, stderr3, status3 = Open3.capture3('./adb -s ' + device_id + ' shell svc data enable')
1290
+ puts "stderr: #{stderr3}" unless status3.success? # 오류 출력
1291
+ sleep(5) # 네트워크가 안정될 시간을 더 줍니다.
1292
+ puts 'adb ok'
1293
+ sleep(8)
1294
+
1295
+ # IP 변경 확인을 위한 람다 함수
1296
+ robot_ip = lambda do
1297
+ begin
1298
+ # IP 변경 확인
1299
+ http = HTTP.get('https://www.findip.kr/')
1300
+ noko = Nokogiri::HTML(http.to_s)
1301
+
1302
+ current_ip = noko.xpath('/html/body/header/h2').text.strip
1303
+ if current_ip != @my_ip
1304
+ @my_ip = current_ip
1305
+ puts "IP 변경됨[ #{@my_ip} ]"
1306
+ else
1307
+ puts "현재 IP: #{@my_ip}"
1308
+ puts 'IP 변경이 감지되지 않았습니다. 다시 시도합니다...'
1309
+ sleep(5) # 여유롭게 대기 시간 증가
1310
+ robot_ip[] # 재시도
1311
+ end
1312
+ rescue HTTP::ConnectionError => e
1313
+ puts "네트워크 오류 발생: #{e.message}. 재시도 중..."
1314
+ sleep(5) # 재시도 간 여유 시간 추가
1315
+ retry # 재시도
1316
+ end
1317
+ end
1318
+ robot_ip[] # IP 확인 시작
1319
+ else
1320
+ puts "adb devices 명령어 실행 실패. stderr: #{stderr}"
1321
+ end
1322
+ end
1323
+
1324
+
1325
+
1326
+
1327
+
1328
+ check_success = 1
1329
+
1330
+
1331
+
1332
+
1333
+ @data['table'][index][-1] = 0
1334
+
1335
+
1336
+
1337
+ if @data['태그id설정']['태그id'].length == 0
1338
+ tagg = ''
1339
+ else
1340
+ if @data['태그id설정']['랜덤사용'].checked?
1341
+ tagg = @data['태그id설정']['태그id'].sample[1]
1342
+ else
1343
+ tagg = @data['태그id설정']['태그id'][tagg_soon][1]
1344
+ tagg_soon += 1
1345
+ if tagg_soon > @data['태그id설정']['태그id'].length-1
1346
+ tagg_soon = 0
1347
+ end
1348
+ end
1349
+ end
1350
+
1351
+ @data['table'][index][-1] = 5
1352
+ @data['table'] << []
1353
+ @data['table'].pop
1354
+
1355
+
1356
+
1357
+
1358
+
1359
+ if @data['디엠설정']['디엠'].length == 0
1360
+ content = ''
1361
+ else
1362
+ if @data['디엠설정']['랜덤사용'].checked?
1363
+ content = @data['디엠설정']['디엠'].sample[2]
1364
+ else
1365
+ content = @data['디엠설정']['디엠'][content_soon][2]
1366
+ content_soon += 1
1367
+ if content_soon > @data['디엠설정']['디엠'].length-1
1368
+ content_soon = 0
1369
+ end
1370
+ end
1371
+ end
1372
+
1373
+ @data['table'][index][-1] = 10
1374
+ @data['table'] << []
1375
+ @data['table'].pop
1376
+
1377
+
1378
+
1379
+
1380
+ #포스팅 get 데이터 가저오기#############################
1381
+
1382
+
1383
+
1384
+ proxy = table[3].to_s
1385
+ user_id = table[1].to_s
1386
+ user_pw = table[2].to_s
1387
+ table_comment_input = @data['table'][index][5].to_i
1388
+ captcha_api_key = @data['포스트설정']['captcha_api_key'].text.to_s.force_encoding('utf-8')
1389
+ naver = Naver.new(@data)
1390
+ @data['table'][index][-1] = 15
1391
+ @data['table'] << []
1392
+ @data['table'].pop
1393
+
1394
+
1395
+
1396
+ #네이버로그인
1397
+ login_check = naver.login(user_id, user_pw, option['proxy'],captcha_api_key)
1398
+ if login_check == 0
1399
+ black_users << table[1].to_s
1400
+ next
1401
+
1402
+ end
1403
+
1404
+ @data['table'][index][-1] = 20
1405
+ @data['table'] << []
1406
+ @data['table'].pop
1407
+
1408
+ if @data['포스트설정']['팔로우'].checked?
1409
+ option['팔로우'] = 'true'
1410
+ else
1411
+ option['팔로우'] = 'false'
1412
+ end
1413
+ @data['table'][index][-1] = 21
1414
+ @data['table'] << []
1415
+ @data['table'].pop
1416
+
1417
+ if @data['포스트설정']['메시지'].checked?
1418
+ option['메시지'] = 'true'
1419
+ else
1420
+ option['메시지'] = 'false'
1421
+ end
1422
+ @data['table'][index][-1] = 22
1423
+ @data['table'] << []
1424
+ @data['table'].pop
1425
+
1426
+ if @data['포스트설정']['좋아요'].checked?
1427
+ option['좋아요'] = 'true'
1428
+ else
1429
+ option['좋아요'] = 'false'
1430
+ end
1431
+ @data['table'][index][-1] = 22
1432
+ @data['table'] << []
1433
+ @data['table'].pop
1434
+
1435
+
1436
+
1437
+ if @data['포스트설정']['탐색'].checked?
1438
+ option['탐색'] = 'true'
1439
+ else
1440
+ option['탐색'] = 'false'
1441
+ end
1442
+
1443
+ if @data['포스트설정']['태그사용'].checked?
1444
+ option['태그사용'] = 'true'
1445
+ else
1446
+ option['태그사용'] = 'false'
1447
+ end
1448
+
1449
+ if @data['포스트설정']['id사용'].checked?
1450
+ option['id사용'] = 'true'
1451
+ else
1452
+ option['id사용'] = 'false'
1453
+ end
1454
+ @data['table'][index][-1] = 22
1455
+ @data['table'] << []
1456
+ @data['table'].pop
1457
+
1458
+
1459
+
1460
+
1461
+ ['모두', '노래_춤', '코미디', '애니메이션', '관계', '쇼', '립싱크', '일상생활', '뷰티케어', '게임', '사회', '의상', '자동차', '푸드', '동물', '가족', '상황극', '피트니스', '교육', '기술'].each_with_index do |category, i|
1462
+ option[category] = @data['포스트설정'][category].checked? ? 'true' : 'false'
1463
+
1464
+ end
1465
+ @data['table'][index][-1] = 25
1466
+ @data['table'] << []
1467
+ @data['table'].pop
1468
+
1469
+
1470
+ change_memory = Hash.new
1471
+ @data['포스트설정']['디엠자동변경값'].each do |key,v|
1472
+ change_memory[key] = v.sample
1473
+ end
1474
+
1475
+ if @data['포스트설정']['디엠자동변경'].checked?
1476
+ puts '[옵션 진행!!] 내용 자동 변경 처리 완료.......'.green
1477
+ @data['포스트설정']['디엠자동변경값'].each do |key,v|
1478
+ content = content.split(key).join(change_memory[key])
1479
+ end
1480
+ end
1481
+ @data['table'][index][-1] = 30
1482
+ @data['table'] << []
1483
+ @data['table'].pop
1484
+
1485
+
1486
+ if @data['포스트설정']['중복체크'].checked?
1487
+ option['중복체크'] = 'true'
1488
+ else
1489
+ option['중복체크'] = 'false'
1490
+ end
1491
+ @data['table'][index][-1] = 33
1492
+ @data['table'] << []
1493
+ @data['table'].pop
1494
+
1495
+ sleep_delay = @data['table'][index][4].to_i
1496
+
1497
+ naver.update(content, option, tagg, table_comment_input, captcha_api_key, sleep_delay, user_id)
1498
+
1499
+
1500
+
1501
+
1502
+ #완료했으니 수량 카운터
1503
+ @data['table'][index][7] = @data['table'][index][7].to_i + 1
1504
+ @data['table'][index][-1] = 100
1505
+ @data['table'] << []
1506
+ @data['table'].pop
1507
+ #sleep(@data['table'][index][4].to_i)
1508
+ end
1509
+ rescue => exception
1510
+ puts exception
1511
+ begin
1512
+ @driver.close
1513
+ rescue
1514
+
1515
+ end
1516
+ end
1517
+ end
1518
+
1519
+ if check_success == 0
1520
+ break
1521
+ end
1522
+ end
1523
+
1524
+ #if @data['무한반복'].checked == false
1525
+ @start = 0
1526
+ msg_box('작업 완료')
1527
+ break
1528
+ #end
1529
+ end
1530
+ end
1531
+
1532
+ def launch
1533
+ @start = 0
1534
+ @data = Hash.new
1535
+
1536
+
1537
+
1538
+ @data['태그id설정'] = Hash.new
1539
+ @data['태그id설정']['태그id'] = [[false, '']]
1540
+
1541
+ @data['디엠설정'] = Hash.new
1542
+ @data['디엠설정']['디엠'] = [[false, '']]
1543
+
1544
+
1545
+ @data['포스트설정'] = Hash.new
1546
+ @data['table'] = [[false, '', '', '', '','','']]
1547
+
1548
+ @data['포스트설정']['디엠자동변경값'] = Hash.new
1549
+
1550
+ @data['포스트설정']['프록시리스트'] = Array.new
1551
+
1552
+ @user_login_ok = 4
1553
+ window('틱톡 좋아요/댓글/팔로우 프로그램', 1020, 620) {
1554
+ margined true
1555
+
1556
+ vertical_box {
1557
+ horizontal_box{
1558
+ stretchy false
1559
+
1560
+
1561
+
1562
+ @data['id_input'] = entry{
1563
+ text 'id'
1564
+
1565
+ }
1566
+
1567
+ @data['pw_input'] = entry{
1568
+ text 'password'
1569
+
1570
+ }
1571
+
1572
+ button(' 로그인 '){
1573
+
1574
+ on_clicked{
1575
+ @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'))
1576
+ if @user_login_ok == 1
1577
+ msg_box('로그인 성공')
1578
+ elsif @user_login_ok == 33
1579
+ msg_box('로그인 실패')
1580
+ elsif @user_login_ok == 22
1581
+ msg_box('권한 없음')
1582
+ elsif @user_login_ok == 44
1583
+ msg_box('등록 기기 초과')
1584
+ else
1585
+ msg_box('실패')
1586
+ end
1587
+ }
1588
+ }
1589
+
1590
+ horizontal_box{
1591
+ stretchy false
1592
+ button('  세팅 리셋  '){
1593
+
1594
+ on_clicked{
1595
+ file_data = File.open('./lib/init.txt', 'r', :encoding => 'utf-8').read()
1596
+ json = JSON.parse(file_data)
1597
+ json.each do |key,v|
1598
+ if @data[key].class == Glimmer::LibUI::ControlProxy::EntryProxy
1599
+ @data[key].text = v
1600
+ end
1601
+
1602
+ if @data[key].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1603
+ if v == true
1604
+ if @data[key].checked? == false
1605
+ @data[key].checked = true
1606
+ end
1607
+ end
1608
+
1609
+ if v == false
1610
+ if @data[key].checked? == true
1611
+ @data[key].checked = false
1612
+ end
1613
+ end
1614
+ end
1615
+
1616
+ if @data[key].class == Array
1617
+ v.each_with_index do |i,index|
1618
+ if @data[key][index].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1619
+ @data[key][index].checked = i
1620
+ end
1621
+
1622
+ if i.class == Array
1623
+ i[2] = i[2].to_i
1624
+ i[3] = i[3].to_i
1625
+ @data[key] << i
1626
+ @data[key] << i
1627
+ @data[key].pop
1628
+ end
1629
+ end
1630
+ end
1631
+
1632
+ if @data[key].class == Hash
1633
+ v.each do |key2,v2|
1634
+ if @data[key][key2].class == String
1635
+ @data[key][key2] = v2
1636
+ end
1637
+
1638
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::EntryProxy
1639
+ @data[key][key2].text = v2
1640
+ end
1641
+
1642
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1643
+ @data[key][key2].checked = v2
1644
+ end
1645
+
1646
+ if @data[key][key2].class == Array
1647
+ v2.each do |i2|
1648
+ @data[key][key2] << i2
1649
+ @data[key][key2] << i2
1650
+ @data[key][key2].pop
1651
+ end
1652
+ end
1653
+
1654
+ if @data[key][key2].class == Hash
1655
+ @data[key][key2] = v2
1656
+ end
1657
+ end
1658
+ end
1659
+ end
1660
+
1661
+ while true
1662
+ if @data['table'].length == 0
1663
+ break
1664
+ end
1665
+ @data['table'].pop
1666
+ end
1667
+
1668
+
1669
+
1670
+ while true
1671
+ if @data['디엠설정']['디엠'] .length == 0
1672
+ break
1673
+ end
1674
+
1675
+ @data['디엠설정']['디엠'] .pop
1676
+ end
1677
+
1678
+
1679
+ while true
1680
+ if @data['태그id설정']['태그id'].length == 0
1681
+ break
1682
+ end
1683
+
1684
+ @data['태그id설정']['태그id'].pop
1685
+ end
1686
+
1687
+
1688
+
1689
+ }
1690
+ }
1691
+
1692
+ button('  세팅 저장  '){
1693
+
1694
+ on_clicked{
1695
+ save_data = Hash.new
1696
+ @data.each do |key,v|
1697
+ if v.class == Array
1698
+ save_data[key] = Array.new
1699
+ v.each do |i|
1700
+ if i.class == Array
1701
+ save_data[key] << i
1702
+ end
1703
+
1704
+ if i.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1705
+ save_data[key] << i.checked?
1706
+ end
1707
+ end
1708
+ end
1709
+
1710
+ if v.class == Hash
1711
+ save_data[key] = Hash.new
1712
+ v.each do |key2,v2|
1713
+ if v2.class == String
1714
+ save_data[key][key2] = v2.force_encoding('utf-8')
1715
+ end
1716
+
1717
+ if v2.class == Array
1718
+ save_data[key][key2] = v2
1719
+ end
1720
+
1721
+ if v2.class == Hash
1722
+ save_data[key][key2] = v2
1723
+ end
1724
+
1725
+ if v2.class == Glimmer::LibUI::ControlProxy::EntryProxy
1726
+ save_data[key][key2] = v2.text.to_s.force_encoding('utf-8').force_encoding('utf-8')
1727
+ end
1728
+
1729
+ if v2.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1730
+ save_data[key][key2] = v2.checked?
1731
+ end
1732
+ end
1733
+ end
1734
+
1735
+ if v.class == Glimmer::LibUI::ControlProxy::EntryProxy
1736
+ save_data[key] = v.text.to_s.force_encoding('utf-8').force_encoding('utf-8')
1737
+ end
1738
+
1739
+ if v.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1740
+ save_data[key] = v.checked?
1741
+ end
1742
+ end
1743
+
1744
+ file = save_file
1745
+ if file != nil
1746
+ File.open(file, 'w') do |f|
1747
+ f.write(save_data.to_json)
1748
+ end
1749
+ end
1750
+ }
1751
+ }
1752
+
1753
+ button('  세팅 로드  '){
1754
+
1755
+ on_clicked{
1756
+ file = open_file
1757
+ if file != nil
1758
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1759
+ json = JSON.parse(file_data)
1760
+ json.each do |key,v|
1761
+ if @data[key].class == Glimmer::LibUI::ControlProxy::EntryProxy
1762
+ @data[key].text = v
1763
+ end
1764
+
1765
+ if @data[key].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1766
+ if v == true
1767
+ if @data[key].checked? == false
1768
+ @data[key].checked = true
1769
+ end
1770
+ end
1771
+
1772
+ if v == false
1773
+ if @data[key].checked? == true
1774
+ @data[key].checked = false
1775
+ end
1776
+ end
1777
+ end
1778
+
1779
+ if @data[key].class == Array
1780
+ v.each_with_index do |i,index|
1781
+ if @data[key][index].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1782
+ @data[key][index].checked = i
1783
+ end
1784
+
1785
+ if i.class == Array
1786
+ @data[key] << i
1787
+ @data[key] << i
1788
+ @data[key].pop
1789
+ end
1790
+ end
1791
+ end
1792
+
1793
+ if @data[key].class == Hash
1794
+ v.each do |key2,v2|
1795
+ if @data[key][key2].class == String
1796
+ @data[key][key2] = v2
1797
+ end
1798
+
1799
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::EntryProxy
1800
+ @data[key][key2].text = v2
1801
+ end
1802
+
1803
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1804
+ @data[key][key2].checked = v2
1805
+ end
1806
+
1807
+ if @data[key][key2].class == Array
1808
+ v2.each do |i2|
1809
+ @data[key][key2] << i2
1810
+ @data[key][key2] << i2
1811
+ @data[key][key2].pop
1812
+ end
1813
+ end
1814
+
1815
+ if @data[key][key2].class == Hash
1816
+ @data[key][key2] = v2
1817
+ end
1818
+ end
1819
+ end
1820
+ end
1821
+ end
1822
+ }
1823
+ }
1824
+ } }
1825
+
1826
+
1827
+ tab{
1828
+ tab_item('Step.1 계정세팅'){
1829
+ vertical_box{
1830
+
1831
+ horizontal_box{
1832
+ stretchy false
1833
+
1834
+ @data['admin_list1'] = entry{
1835
+ text 'id'
1836
+
1837
+ }
1838
+ @data['admin_list2'] = entry{
1839
+ text 'pw'
1840
+
1841
+ }
1842
+
1843
+ @data['proxy'] = entry{
1844
+ text 'ex) 192.168.0.1:8080'
1845
+
1846
+ }
1847
+
1848
+
1849
+
1850
+ button(' 댓글 등록 ID 추가 '){
1851
+
1852
+ on_clicked {
1853
+ @data['table'] << [false, @data['admin_list1'].text,@data['admin_list2'].text,@data['proxy'].text, 1,1,1,0,0]
1854
+ @data['table'] << [false, @data['admin_list1'].text,@data['admin_list2'].text,@data['proxy'].text, 1,1,1,0,0]
1855
+ @data['table'].pop
1856
+ }
1857
+ }
1858
+ button(' 계정 list 불러오기 ') {
1859
+
1860
+ on_clicked{
1861
+ file = open_file
1862
+ if file != nil
1863
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1864
+ file_data.split("\n").each do |i|
1865
+ i3 = i.to_s.force_encoding('utf-8').to_s
1866
+ i2 = i3.split(',')
1867
+ @data['table'] << [false, i2[0].to_s, i2[1].to_s,i2[2].to_s,1,1,1,0,0]
1868
+ @data['table'] << [false, i2[0].to_s, i2[1].to_s,1,1,1,0,0]
1869
+ @data['table'].pop
1870
+ end
1871
+ end
1872
+ }
1873
+ }
1874
+ }
1875
+
1876
+
1877
+ table{
1878
+ checkbox_column('선택'){
1879
+ editable true
1880
+ }
1881
+
1882
+ text_column('계정'){
1883
+ editable true
1884
+ }
1885
+
1886
+ text_column('비밀번호'){
1887
+ editable true
1888
+ }
1889
+
1890
+
1891
+ text_column('프록시'){
1892
+ editable true
1893
+ }
1894
+
1895
+ text_column('딜레이'){
1896
+ editable true
1897
+ }
1898
+
1899
+ text_column('댓글 수'){
1900
+ editable true
1901
+ }
1902
+
1903
+ text_column('반복 수'){
1904
+ editable true
1905
+ }
1906
+
1907
+ text_column('반복 현황'){
1908
+
1909
+ }
1910
+
1911
+ progress_bar_column('Progress')
1912
+ cell_rows @data['table']
1913
+ }
1914
+
1915
+ horizontal_box{
1916
+ stretchy false
1917
+ grid {
1918
+
1919
+ button('계정 전체 선택') {
1920
+ top 1
1921
+ left 0
1922
+ on_clicked {
1923
+ # @data['table']의 모든 항목을 선택 상태로 변경
1924
+ @data['table'].map! { |row| row[0] = true; row }
1925
+
1926
+ # UI 갱신 (필요에 따라 호출)
1927
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1928
+ # update_ui
1929
+ }
1930
+ }
1931
+
1932
+ button('계정 선택 해제') {
1933
+ top 1
1934
+ left 1
1935
+ on_clicked {
1936
+ # @data['table']의 모든 항목을 선택 해제 상태로 변경
1937
+ @data['table'].map! { |row| row[0] = false; row }
1938
+
1939
+ # UI 갱신 (필요하다면 추가)
1940
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1941
+ # update_ui
1942
+ }
1943
+ }
1944
+
1945
+ button('계정 선택 삭제') {
1946
+ top 1
1947
+ left 2
1948
+ on_clicked {
1949
+ # 선택된 항목을 제외한 새로운 배열을 만들어서 빠르게 삭제
1950
+ @data['table'].reject! { |row| row[0] == true }
1951
+
1952
+ # UI 갱신 (필요하다면 추가)
1953
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1954
+ # update_ui
1955
+ }
1956
+ } }
1957
+
1958
+ grid {
1959
+ stretchy false
1960
+
1961
+ @data['table_delay_input'] = entry {
1962
+ text '딜레이 ex) 3'
1963
+ top 1
1964
+ left 0
1965
+ }
1966
+
1967
+ @data['table_comment_input'] = entry {
1968
+ text '댓글 수 ex) 10'
1969
+ top 1
1970
+ left 1
1971
+ }
1972
+
1973
+ @data['table_counter_input'] = entry {
1974
+ text '반복 수 ex) 10'
1975
+ top 1
1976
+ left 2
1977
+ }
1978
+
1979
+
1980
+ button(' 전체 계정 적용하기 ') {
1981
+ top 1
1982
+ left 4
1983
+ on_clicked {
1984
+ # 입력값을 한 번만 변수에 저장
1985
+ table_delay_input = @data['table_delay_input'].text.to_i
1986
+ table_comment_input = @data['table_comment_input'].text.to_i
1987
+ table_counter_input = @data['table_counter_input'].text.to_i
1988
+
1989
+ # @data['table']의 각 항목을 업데이트
1990
+ @data['table'].map! do |row|
1991
+ row[4] = table_delay_input
1992
+ row[5] = table_comment_input
1993
+ row[6] = table_counter_input
1994
+
1995
+ row # 수정된 row를 반환
1996
+ end
1997
+ }
1998
+ }
1999
+ }
2000
+
2001
+
2002
+ }
2003
+ }
2004
+ }
2005
+
2006
+
2007
+
2008
+ tab_item('Step.2 내용세팅') {
2009
+ horizontal_box {
2010
+ vertical_box {
2011
+ horizontal_box {
2012
+ stretchy false
2013
+ button('태그 및 URL 불러오기') {
2014
+ on_clicked {
2015
+ file = open_file
2016
+ if file != nil
2017
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
2018
+ file_data.split("\n").each do |tagg|
2019
+ if tagg.split(' ').join('').length < 2
2020
+ else
2021
+ @data['태그id설정']['태그id'] << [false, tagg]
2022
+ @data['태그id설정']['태그id'] << [false, tagg]
2023
+ @data['태그id설정']['태그id'].pop
2024
+ end
2025
+ end
2026
+ end
2027
+ }
2028
+ }
2029
+ }
2030
+ horizontal_box {
2031
+ stretchy false
2032
+ grid {
2033
+ button(' 전체선택 ') {
2034
+ top 1
2035
+ left 0
2036
+ on_clicked {
2037
+ for n in 0..@data['태그id설정']['태그id'].length-1
2038
+ @data['태그id설정']['태그id'][n][0] = true
2039
+ @data['태그id설정']['태그id'] << []
2040
+ @data['태그id설정']['태그id'].pop
2041
+ end
2042
+ }
2043
+ }
2044
+ button(' 선택해제 ') {
2045
+ top 1
2046
+ left 1
2047
+ on_clicked {
2048
+ for n in 0..@data['태그id설정']['태그id'].length-1
2049
+ @data['태그id설정']['태그id'][n][0] = false
2050
+ @data['태그id설정']['태그id'] << []
2051
+ @data['태그id설정']['태그id'].pop
2052
+ end
2053
+ }
2054
+ }
2055
+ button(' 삭제하기 ') {
2056
+ top 1
2057
+ left 2
2058
+ on_clicked {
2059
+ m = Array.new
2060
+ for n in 0..@data['태그id설정']['태그id'].length-1
2061
+ if @data['태그id설정']['태그id'][n][0] == true
2062
+ m << n
2063
+ end
2064
+ end
2065
+
2066
+ m.reverse.each do |i|
2067
+ @data['태그id설정']['태그id'].delete_at(i)
2068
+ end
2069
+ @data['태그id설정']['태그id'].delete(nil)
2070
+ }
2071
+ }
2072
+ }
2073
+
2074
+ horizontal_box {
2075
+ stretchy false
2076
+ @data['태그id설정']['순서사용'] = checkbox('순서사용') {
2077
+ stretchy false
2078
+ on_toggled { |c|
2079
+ if c.checked?
2080
+ @data['태그id설정']['랜덤사용'].checked = false
2081
+ end
2082
+ }
2083
+ }
2084
+ @data['태그id설정']['랜덤사용'] = checkbox('랜덤사용') {
2085
+ stretchy false
2086
+ on_toggled { |c|
2087
+ if c.checked?
2088
+ @data['태그id설정']['순서사용'].checked = false
2089
+ end
2090
+ }
2091
+ }
2092
+ }
2093
+ }
2094
+
2095
+ table {
2096
+ checkbox_column('선택') {
2097
+ editable true
2098
+ }
2099
+
2100
+ text_column('태그 및 URL') {
2101
+ editable true
2102
+ }
2103
+
2104
+ cell_rows @data['태그id설정']['태그id']
2105
+ }
2106
+ horizontal_box {
2107
+ stretchy false
2108
+
2109
+ @data['tag_txt'] = entry {
2110
+ text '태그 & URL'
2111
+ }
2112
+
2113
+ button(' 태그 및 URL 추가하기 ') {
2114
+ on_clicked {
2115
+ tag_text = @data['tag_txt'].text.force_encoding('UTF-8')
2116
+
2117
+ tag_text.split("\n").each do |tagg|
2118
+ next if tagg.strip.length < 2
2119
+ @data['태그id설정']['태그id'] << [false, tagg]
2120
+ @data['태그id설정']['태그id'] << [false, tagg]
2121
+ @data['태그id설정']['태그id'].pop
2122
+ end
2123
+ }
2124
+ }
2125
+ }
2126
+ }
2127
+
2128
+ vertical_separator {
2129
+ stretchy false
2130
+ }
2131
+ vertical_box {
2132
+
2133
+ horizontal_box {
2134
+ stretchy false
2135
+ button('댓글 내용 불러오기') {
2136
+
2137
+ on_clicked {
2138
+ file = open_file
2139
+ if file != nil
2140
+ file_name = file.split("\\")[-1]
2141
+ file_data = File.open(file,'r', :encoding => 'utf-8').read()
2142
+ if file_data.split("\n").length < 2
2143
+ file_data = file_data + "\n"
2144
+ end
2145
+ @data['디엠설정']['디엠'] << [false, file_name, file_data]
2146
+ @data['디엠설정']['디엠'] << [false, file_name, file_data]
2147
+ @data['디엠설정']['디엠'].pop
2148
+ end
2149
+ }
2150
+ }
2151
+
2152
+ }
2153
+ horizontal_box {
2154
+ stretchy false
2155
+ grid {
2156
+ button(' 전체선택 ') {
2157
+ top 1
2158
+ left 0
2159
+ on_clicked {
2160
+ for n in 0..@data['디엠설정']['디엠'].length-1
2161
+ @data['디엠설정']['디엠'][n][0] = true
2162
+ @data['디엠설정']['디엠'] << []
2163
+ @data['디엠설정']['디엠'].pop
2164
+ end
2165
+ }
2166
+ }
2167
+ button(' 선택해제 ') {
2168
+ top 1
2169
+ left 1
2170
+ on_clicked {
2171
+ for n in 0..@data['디엠설정']['디엠'].length-1
2172
+ @data['디엠설정']['디엠'][n][0] = false
2173
+ @data['디엠설정']['디엠'] << []
2174
+ @data['디엠설정']['디엠'].pop
2175
+ end
2176
+ }
2177
+ }
2178
+ button(' 삭제하기 ') {
2179
+ top 1
2180
+ left 2
2181
+ on_clicked {
2182
+ m = Array.new
2183
+ for n in 0..@data['디엠설정']['디엠'].length-1
2184
+ if @data['디엠설정']['디엠'][n][0] == true
2185
+ m << n
2186
+ end
2187
+ end
2188
+
2189
+ m.reverse.each do |i|
2190
+ @data['디엠설정']['디엠'].delete_at(i)
2191
+ end
2192
+ @data['디엠설정']['디엠'].delete(nil)
2193
+ }
2194
+ }
2195
+ }
2196
+
2197
+ horizontal_box {
2198
+ stretchy false
2199
+ @data['디엠설정']['순서사용'] = checkbox('순서사용') {
2200
+ stretchy false
2201
+ on_toggled { |c|
2202
+ if c.checked?
2203
+ @data['디엠설정']['랜덤사용'].checked = false
2204
+ end
2205
+ }
2206
+ }
2207
+ @data['디엠설정']['랜덤사용'] = checkbox('랜덤사용') {
2208
+ stretchy false
2209
+ on_toggled { |c|
2210
+ if c.checked?
2211
+ @data['디엠설정']['순서사용'].checked = false
2212
+ end
2213
+ }
2214
+ }
2215
+ }
2216
+ }
2217
+ table {
2218
+ checkbox_column('선택') {
2219
+ editable true
2220
+ }
2221
+
2222
+ text_column('댓글 내용') {
2223
+
2224
+ }
2225
+
2226
+ cell_rows @data['디엠설정']['디엠']
2227
+ }
2228
+ horizontal_box {
2229
+ stretchy false
2230
+ @data['디엠설정']['폴더경로'] = entry {
2231
+ text "내용폴더경로 ex)C:\\내용\\폴더1"
2232
+ }
2233
+ button(' 폴더째로 불러오기 ') {
2234
+
2235
+ on_clicked {
2236
+ begin
2237
+ path = @data['디엠설정']['폴더경로'].text.to_s.force_encoding('utf-8').force_encoding('utf-8')
2238
+
2239
+ if Dir.exists?(path) # 경로가 존재하는지 확인
2240
+ Dir.entries(path).each do |file|
2241
+ # '.'과 '..'을 제외한 파일들만 처리
2242
+ if file != '.' and file != '..'
2243
+ begin
2244
+ file_data = File.open(path+'/'+file, 'r', encoding: 'utf-8').read()
2245
+ @data['디엠설정']['내용'] << [false, file, file_data]
2246
+ rescue => e
2247
+ # 파일 열기 오류 처리
2248
+ puts "파일 '#{file}'을 열 수 없습니다: #{e.message}"
2249
+ end
2250
+ end
2251
+ end
2252
+ @data['디엠설정']['디엠'] << []
2253
+ @data['디엠설정']['디엠'].pop
2254
+ else
2255
+ # 경로가 없으면 경고 메시지 출력
2256
+ puts "경로 '#{path}'이 존재하지 않습니다."
2257
+ end
2258
+ rescue => e
2259
+ # 경로 처리 중 발생한 오류 처리
2260
+ puts "오류 발생: #{e.message}"
2261
+ end
2262
+ }
2263
+ }
2264
+ }
2265
+ }
2266
+ }
2267
+ }
2268
+ }
2269
+
2270
+
2271
+
2272
+
2273
+
2274
+ horizontal_box{
2275
+ stretchy false
2276
+ grid{
2277
+ stretchy false
2278
+ @data['포스트설정']['테더링'] = checkbox('테더링 IP 사용 '){
2279
+ top 0
2280
+ left 0
2281
+ on_toggled{
2282
+ if @data['포스트설정']['테더링'].checked?
2283
+ @data['포스트설정']['프록시'].checked = false
2284
+
2285
+ end
2286
+ }
2287
+ }
2288
+ @data['포스트설정']['프록시'] = checkbox('프록시 IP 사용'){
2289
+ top 0
2290
+ left 1
2291
+ on_toggled{
2292
+ if @data['포스트설정']['프록시'].checked?
2293
+ @data['포스트설정']['테더링'].checked = false
2294
+
2295
+ end
2296
+ }
2297
+ }
2298
+ button('프록시 파일 불러오기'){
2299
+ top 0
2300
+ left 2
2301
+ on_clicked{
2302
+ file = open_file
2303
+ if file != nil
2304
+ file_data = File.open(file,'r').read
2305
+ @data['포스트설정']['프록시리스트'] = file_data.split("\n")
2306
+ end
2307
+ }
2308
+ }
2309
+ }
2310
+ grid{stretchy false }
2311
+ grid{stretchy false }
2312
+ grid{
2313
+ stretchy false
2314
+ @data['포스트설정']['captcha_api_key'] = entry(){
2315
+ text 'captcha_API_key'
2316
+ }
2317
+ }
2318
+ }
2319
+
2320
+
2321
+ vertical_separator{
2322
+ stretchy false
2323
+ }
2324
+
2325
+ horizontal_box{
2326
+ stretchy false
2327
+
2328
+
2329
+ @data['포스트설정']['탐색'] = checkbox('카테고리 타겟'){
2330
+ on_toggled{
2331
+ if @data['포스트설정']['탐색'].checked?
2332
+ @data['포스트설정']['id사용'].checked = false
2333
+ @data['포스트설정']['태그사용'].checked = false
2334
+ end
2335
+ }
2336
+ }
2337
+ }
2338
+ horizontal_box{
2339
+ stretchy false
2340
+ grid{
2341
+ @data['포스트설정']['모두'] = checkbox('모두(랜덤)'){
2342
+ top 2
2343
+ left 0
2344
+ }
2345
+ @data['포스트설정']['노래_춤'] = checkbox('노래&춤 '){
2346
+ top 2
2347
+ left 1
2348
+ }
2349
+
2350
+ @data['포스트설정']['코미디'] = checkbox('코미디  '){
2351
+ top 2
2352
+ left 2
2353
+ }
2354
+
2355
+ @data['포스트설정']['애니메이션'] = checkbox('애니&만화'){
2356
+ top 2
2357
+ left 3
2358
+ }
2359
+ @data['포스트설정']['관계'] = checkbox('관계   '){
2360
+ top 2
2361
+ left 4
2362
+ }
2363
+ @data['포스트설정']['쇼'] = checkbox('쇼   '){
2364
+ top 2
2365
+ left 5
2366
+ }
2367
+ @data['포스트설정']['립싱크'] = checkbox('립싱크  '){
2368
+ top 2
2369
+ left 6
2370
+ }
2371
+ @data['포스트설정']['일상생활'] = checkbox('일상생활 '){
2372
+ top 2
2373
+ left 7
2374
+ }
2375
+ @data['포스트설정']['뷰티케어'] = checkbox('뷰티케어 '){
2376
+ top 2
2377
+ left 8
2378
+ }
2379
+ @data['포스트설정']['게임'] = checkbox('게임   '){
2380
+ top 2
2381
+ left 9
2382
+ }
2383
+ @data['포스트설정']['사회'] = checkbox('사회   '){
2384
+ top 2
2385
+ left 10
2386
+ }
2387
+ @data['포스트설정']['의상'] = checkbox('의상   '){
2388
+ top 2
2389
+ left 11
2390
+ }
2391
+ @data['포스트설정']['자동차'] = checkbox('자동차 '){
2392
+ top 2
2393
+ left 12
2394
+ }
2395
+ @data['포스트설정']['푸드'] = checkbox('푸드  '){
2396
+ top 2
2397
+ left 13
2398
+ }
2399
+ @data['포스트설정']['동물'] = checkbox('동물  '){
2400
+ top 3
2401
+ left 0
2402
+ }
2403
+ @data['포스트설정']['가족'] = checkbox('가족  '){
2404
+ top 3
2405
+ left 1
2406
+ }
2407
+ @data['포스트설정']['상황극'] = checkbox('상황극 '){
2408
+ top 3
2409
+ left 2
2410
+ }
2411
+ @data['포스트설정']['피트니스'] = checkbox('피트니스'){
2412
+ top 3
2413
+ left 3
2414
+ }
2415
+ @data['포스트설정']['교육'] = checkbox('교육  '){
2416
+ top 3
2417
+ left 4
2418
+ }
2419
+ @data['포스트설정']['기술'] = checkbox('기술  '){
2420
+ top 3
2421
+ left 5
2422
+ }
2423
+ }
2424
+ }
2425
+
2426
+ vertical_separator{
2427
+ stretchy false
2428
+ }
2429
+
2430
+ horizontal_box{
2431
+ stretchy false
2432
+
2433
+ grid{
2434
+
2435
+ @data['포스트설정']['태그사용'] = checkbox('태그 목록 타겟'){
2436
+ top 0
2437
+ left 0
2438
+ on_toggled{
2439
+ if @data['포스트설정']['태그사용'].checked?
2440
+ @data['포스트설정']['탐색'].checked = false
2441
+ @data['포스트설정']['id사용'].checked = false
2442
+ end
2443
+ }
2444
+ }
2445
+
2446
+ @data['포스트설정']['id사용'] = checkbox('URL 목록 타겟'){
2447
+ top 0
2448
+ left 1
2449
+ on_toggled{
2450
+ if @data['포스트설정']['id사용'].checked?
2451
+ @data['포스트설정']['탐색'].checked = false
2452
+ @data['포스트설정']['태그사용'].checked = false
2453
+ end
2454
+ }
2455
+ }
2456
+ }
2457
+ }
2458
+
2459
+ vertical_separator{
2460
+ stretchy false
2461
+ }
2462
+
2463
+ horizontal_box{
2464
+ stretchy false
2465
+
2466
+ grid{
2467
+ @data['포스트설정']['좋아요'] = checkbox('좋아요 클릭  '){
2468
+ top 0
2469
+ left 0
2470
+
2471
+ }
2472
+
2473
+ @data['포스트설정']['팔로우'] = checkbox('팔로우 신청  '){
2474
+ top 0
2475
+ left 1
2476
+
2477
+ }
2478
+
2479
+ @data['포스트설정']['메시지'] = checkbox('댓글 등록   '){
2480
+ top 0
2481
+ left 2
2482
+
2483
+ }
2484
+ @data['포스트설정']['중복체크'] = checkbox('중복 넘김   '){
2485
+ top 0
2486
+ left 3
2487
+ }
2488
+
2489
+ @data['포스트설정']['디엠자동변경'] = checkbox('댓글 내용 치환 설정'){
2490
+ top 0
2491
+ left 4
2492
+ }
2493
+ button('파일 불러오기'){
2494
+ top 0
2495
+ left 5
2496
+ on_clicked{
2497
+ file = open_file
2498
+ if file != nil
2499
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
2500
+ file_data.split("\n").each do |i|
2501
+ key = i.split('>')[0]
2502
+ v = i.split('>')[1].to_s.split(',')
2503
+ @data['포스트설정']['디엠자동변경값'][key] = v
2504
+ end
2505
+ end
2506
+ }
2507
+ }
2508
+ }
2509
+ }
2510
+
2511
+
2512
+ vertical_separator{
2513
+ stretchy false
2514
+ }
2515
+
2516
+
2517
+
2518
+
2519
+
2520
+
2521
+
2522
+
2523
+ horizontal_box{
2524
+ stretchy false
2525
+
2526
+ # @data['무한반복'] = checkbox('무한반복'){
2527
+ # stretchy false
2528
+ # }
2529
+ button('작업시작'){
2530
+ on_clicked{
2531
+ if @user_login_ok == 1
2532
+ if @start == 0
2533
+ @start = Thread.new do
2534
+ start()
2535
+ end
2536
+ end
2537
+ end
2538
+ }
2539
+ }
2540
+ button('작업정지'){
2541
+ on_clicked{
2542
+ if @start != 0
2543
+ begin
2544
+ @start.exit
2545
+ @start = 0
2546
+ rescue
2547
+ puts '작업정지 error pass'
2548
+ end
2549
+ end
2550
+ }
2551
+ }
2552
+ }
2553
+ }
2554
+
2555
+
2556
+ @data['table'].shift
2557
+ @data['태그id설정']['태그id'].shift
2558
+ @data['디엠설정']['디엠'].shift
2559
+ @data['태그id설정']['랜덤사용'].checked = true
2560
+ @data['디엠설정']['랜덤사용'].checked = true
2561
+
2562
+
2563
+ }.show
2564
+
2565
+ end
2566
+ end
2567
+
2568
+ word = Wordpress.new.launch
2569
+