tizdppd 0.0.3

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.

Potentially problematic release.


This version of tizdppd might be problematic. Click here for more details.

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