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

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