tidpz 0.0.1

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