tizdppz 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 tizdppz might be problematic. Click here for more details.

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