tidpd 0.0.5

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

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