tblog_duopack 0.0.39 → 0.0.51

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 +4 -4
  2. data/lib/tblog_duopack.rb +303 -55
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44cd103f7388e3c3b8d273b70b1e8e922d9c943f75c15b954f3c6b51bf9cae98
4
- data.tar.gz: 96fe769e09a7a8dfd5654c4d12d9eede886dd61a1187ab149a9088a1d7257ac9
3
+ metadata.gz: ad9d079f87010d2cb76d806d622812ff121b447c50a53281c5405638a4ec8b3c
4
+ data.tar.gz: d272c07dac84708c42baf4a2130c3c119ef81cc314ceeb6a0b5f3437db35beb4
5
5
  SHA512:
6
- metadata.gz: 005f2424612bab332af9179a7e25a49c7795ad52e2e4ab1a3b3da0d0599b4db504b7fe66e4be823a8f2bd8baf3397cfce4468d6a41dfaf531e4cabf502df83bf
7
- data.tar.gz: 2201f3fdcfc633f45fddabee86f78d80ea6299c5acaba2eb0b65b88fe85180e68114e3ffee8047757bd39d8a7e0ff3c3fe9bb18e82f71ea5c52a530de95293f7
6
+ metadata.gz: ebd34ad67bc3c9a4fbf66cb36c111f2f3c9590bfbd0a7e12cbcdde713cfdf96d9e682cc04c4dceea9ab1bcb0ffafd984e262294f8eba574e9ba6ccde21760972
7
+ data.tar.gz: aeac5ac03199e03ddc17e0a1c78231f2c429656817e5bfa8cca8aa22ea2e6076f6ceaf278c9f83837703716440ecbb80e4d3261f8952e9ec66d0bd921408ea0d
data/lib/tblog_duopack.rb CHANGED
@@ -15,6 +15,8 @@ require 'uri'
15
15
  require 'cgi'
16
16
  require 'auto_click'
17
17
  require 'rainbow/refinement'
18
+ require 'httpclient'
19
+ require 'win32ole'
18
20
  include AutoClickMethods
19
21
  using Rainbow
20
22
  include Glimmer
@@ -241,9 +243,50 @@ end
241
243
 
242
244
  class Naver
243
245
  def initialize
244
- @seed = 1
245
- @cookie = ''
246
+ @seed = 1
247
+ @cookie = ''
248
+ kill_selenium_chrome #기존 창 모두 닫는 명령
249
+ sleep(1)
250
+ end
251
+
252
+ def kill_selenium_chrome
253
+ wmi = WIN32OLE.connect("winmgmts://")
254
+
255
+ # chromedriver 프로세스 목록을 Ruby 배열로 변환
256
+ chromedrivers_ole = wmi.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'chromedriver.exe'")
257
+ chromedrivers = []
258
+ chromedrivers_ole.each { |proc| chromedrivers << proc }
259
+ chromedriver_pids = chromedrivers.map { |proc| proc.ProcessId }
260
+
261
+ # chrome.exe 프로세스 목록을 Ruby 배열로 변환
262
+ chrome_procs_ole = wmi.ExecQuery("SELECT * FROM Win32_Process WHERE Name = 'chrome.exe'")
263
+ chrome_procs = []
264
+ chrome_procs_ole.each { |proc| chrome_procs << proc }
265
+
266
+ # chromedriver가 띄운 크롬 종료
267
+ chrome_procs.each do |proc|
268
+ if chromedriver_pids.include?(proc.ParentProcessId)
269
+ puts "→ chromedriver가 띄운 크롬 종료: PID #{proc.ProcessId}"
270
+ begin
271
+ proc.Terminate
272
+ rescue => e
273
+ puts "→ 종료 실패: PID #{proc.ProcessId} / 오류: #{e.message}"
274
+ end
275
+ end
276
+ end
277
+
278
+ # chromedriver 자체도 종료
279
+ chromedrivers.each do |proc|
280
+ puts "→ chromedriver 종료: PID #{proc.ProcessId}"
281
+ begin
282
+ proc.Terminate
283
+ rescue => e
284
+ puts "→ chromedriver 종료 실패: #{proc.ProcessId} / 오류: #{e.message}"
285
+ end
286
+ end
246
287
  end
288
+
289
+
247
290
 
248
291
  def chrome_start(proxy)
249
292
  # 공통 옵션 설정
@@ -261,7 +304,6 @@ class Naver
261
304
  end
262
305
  options = Selenium::WebDriver::Chrome::Options.new
263
306
  options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
264
- options.add_extension('./crx/app.crx') # 확장 프로그램을 첫 번째 탭에 추가
265
307
  options.add_argument('--disable-blink-features=AutomationControlled')
266
308
  options.add_argument('--disable-popup-blocking')
267
309
  options.add_argument('--dns-prefetch-disable')
@@ -370,25 +412,103 @@ class Naver
370
412
  @driver.switch_to.new_window(:tab)
371
413
  sleep(1.5)
372
414
  @driver.switch_to.window(@driver.window_handles[1])
373
- @driver.navigate.to("chrome-extension://ifibfemgeogfhoebkmokieepdoobkbpo/options/options.html")
415
+ @driver.navigate.to("https://chromewebstore.google.com/detail/captcha-solver-auto-recog/ifibfemgeogfhoebkmokieepdoobkbpo?hl=ko")
374
416
  sleep(3)
375
- @driver.find_element(:xpath, '//*[@name="apiKey"]').click
376
- Clipboard.copy(captcha_api_key)
377
- sleep(0.5)
378
- @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
379
- sleep(0.5)
380
- @driver.find_element(:xpath, '//*[@data-lang="login"]').click
381
-
382
417
  begin
383
- sleep(2)
384
- @driver.switch_to.alert.dismiss
385
- sleep(1)
418
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
419
+ wait.until { @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="ajZLRd"]') } #추가 되어 있음
420
+ captcha_solver = 0
421
+ puts'[Step.01] CAPTCHA 세션 및 브라우저 설정 완료 상태 확인!!.......'.yellow
386
422
  rescue
387
- end
388
- @driver.close
389
- sleep(1)
390
- @driver.switch_to.window(@driver.window_handles[0])
391
- sleep(2)
423
+ begin
424
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
425
+ wait.until { @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="wQO0od"]') } #추가 안되어 있음
426
+ sleep(1.5)
427
+ @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="wQO0od"]').click
428
+ puts'[Step.01] CAPTCHA 세션 연결 없음!! 브라우저 필요 설정 미 완료 상태!!.......'.red
429
+ puts'[Step.02] CAPTCHA 세션 연결 및 브라우저 필요 설정 진행 시작!!.......'.red
430
+ sleep(1.5)
431
+ shell = WIN32OLE.new('WScript.Shell')
432
+ shell.AppActivate("Captcha Solver: Auto Recognition and Bypass")
433
+ shell.AppActivate("Captcha Solver: Auto Recognition and Bypass")
434
+ sleep(1)
435
+ shell.SendKeys("{TAB}")
436
+ sleep(0.5)
437
+ shell.SendKeys("{ENTER}")
438
+
439
+ captcha_solver = 1
440
+ sleep(1)
441
+ rescue
442
+ @driver.quit
443
+ sleep(1)
444
+ @driver.close
445
+ return 0
446
+ sleep(1)
447
+ end
448
+ end
449
+ if captcha_solver == 1
450
+ begin
451
+ @driver.get('chrome-extension://ifibfemgeogfhoebkmokieepdoobkbpo/options/options.html')
452
+ sleep(5)
453
+ @driver.get('chrome-extension://ifibfemgeogfhoebkmokieepdoobkbpo/options/options.html')
454
+ sleep(1)
455
+ # 요소 찾기 타임아웃을 10초로 설정
456
+ wait = Selenium::WebDriver::Wait.new(:timeout => 5)
457
+ #요소가 나타날 때까지 60초 동안 기다립니다.
458
+ wait.until { @driver.find_element(:xpath, '/html/body/div/div[1]/table/tbody/tr[1]/td[2]/input') }
459
+ @driver.find_element(:xpath, '/html/body/div/div[1]/table/tbody/tr[1]/td[2]/input').click
460
+ sleep(1)
461
+ Clipboard.copy(captcha_api_key)
462
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
463
+ sleep(1)
464
+ @driver.find_element(:xpath, '//*[@id="connect"]').click
465
+ sleep(1)
466
+
467
+ begin
468
+ wait = Selenium::WebDriver::Wait.new(:timeout => 10)
469
+ wait.until do
470
+ begin
471
+ alert = @driver.switch_to.alert
472
+ alert.accept
473
+ sleep(1)
474
+ true
475
+ rescue Selenium::WebDriver::Error::NoSuchAlertError
476
+ false
477
+ end
478
+ end
479
+ sleep(1)
480
+ @driver.close
481
+ sleep(1)
482
+ @driver.switch_to.window(@driver.window_handles[0])
483
+ sleep(2)
484
+ rescue Selenium::WebDriver::Error::TimeoutError
485
+ @driver.find_element(:xpath, '//*[@id="connect"]').click
486
+ begin
487
+ wait = Selenium::WebDriver::Wait.new(:timeout => 10)
488
+ wait.until do
489
+ begin
490
+ alert = @driver.switch_to.alert
491
+ alert.accept
492
+ sleep(1)
493
+ true
494
+ rescue Selenium::WebDriver::Error::NoSuchAlertError
495
+ false
496
+ end
497
+ end
498
+ rescue Selenium::WebDriver::Error::TimeoutError
499
+ puts "캡챠 기능이 완벽하게 세팅되지않아 캡챠 발생시 해제가 안될수있습니다.".red
500
+ puts "가급적 캡챠 기능 세팅시 PC 사용을 멈춰주세요!".red
501
+ sleep(1)
502
+ @driver.close
503
+ sleep(1)
504
+ @driver.switch_to.window(@driver.window_handles[0])
505
+ sleep(2)
506
+ end
507
+ end
508
+ rescue
509
+ end
510
+ else
511
+ end
392
512
  else
393
513
  end
394
514
 
@@ -1636,21 +1756,81 @@ class Wordpress
1636
1756
 
1637
1757
 
1638
1758
 
1639
- def auto_image
1759
+ def auto_image(keyword = nil)
1760
+ keyword ||= @keyword
1761
+ puts "키워드: #{keyword}"
1762
+
1763
+ client = HTTPClient.new
1764
+ client.default_header = {
1765
+ 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '\
1766
+ '(KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',
1767
+ 'Accept' => 'application/json, text/javascript, */*; q=0.01',
1768
+ 'Accept-Language' => 'en-US,en;q=0.9',
1769
+ 'Referer' => "https://unsplash.com/s/photos/#{URI.encode_www_form_component(keyword)}",
1770
+ 'X-Requested-With' => 'XMLHttpRequest'
1771
+ }
1772
+
1773
+ retry_count = 0
1774
+ max_retries = 10
1775
+ results = []
1776
+
1640
1777
  begin
1641
1778
  page = rand(1..15)
1642
- http = HTTP.get('https://unsplash.com/napi/photos?per_page=12&page='+page.to_s)
1643
- json = JSON.parse(http.to_s)
1644
- mm = Array.new
1645
- json.each do |i|
1646
- mm << i['urls']['full']
1779
+ url = "https://unsplash.com/napi/search/photos?query=#{URI.encode_www_form_component(keyword)}&page=#{page}&per_page=20"
1780
+ puts "Request URL: #{url}"
1781
+ res = client.get(url)
1782
+
1783
+ unless res.status == 200
1784
+ puts "HTTP Error: #{res.status}"
1785
+ raise "HTTP Error"
1647
1786
  end
1648
- url = mm.sample
1649
- Down.download(url, destination: "./image/memory.png")
1650
- rescue
1651
- puts 'auto_image 일시적 error 5초후 제시도...'
1652
- sleep(5)
1787
+
1788
+ json = JSON.parse(res.body)
1789
+ results = json['results']
1790
+ mm = []
1791
+
1792
+ results.each do |photo|
1793
+ full_url = photo.dig('urls', 'full').to_s
1794
+ regular_url = photo.dig('urls', 'regular').to_s
1795
+
1796
+ if full_url.start_with?("https://images.unsplash.com/photo-") &&
1797
+ regular_url.include?("1080")
1798
+ mm << full_url
1799
+ end
1800
+ end
1801
+
1802
+ if mm.empty?
1803
+ raise "No matching image"
1804
+ end
1805
+
1806
+ selected_url = mm.sample
1807
+ Down.download(selected_url, destination: "./image/memory.png")
1808
+ puts "이미지 다운로드 완료: #{selected_url}"
1809
+
1810
+ rescue => e
1811
+ retry_count += 1
1812
+ puts "auto_image 에러: #{e.message} (재시도 #{retry_count}/#{max_retries})"
1813
+ sleep(3)
1814
+ if retry_count < max_retries
1653
1815
  retry
1816
+ else
1817
+ puts "최대 재시도 초과. 조건 무시하고 랜덤 이미지 다운로드 시도..."
1818
+
1819
+ if results && !results.empty?
1820
+ random_photo = results.sample
1821
+ fallback_url = random_photo.dig('urls', 'full')
1822
+ if fallback_url
1823
+ Down.download(fallback_url, destination: "./image/memory.png")
1824
+ puts "랜덤 이미지 다운로드 완료: #{fallback_url}"
1825
+ else
1826
+ puts "랜덤 이미지 URL을 찾을 수 없습니다. 단색 배경 이미지 생성합니다."
1827
+ color_image
1828
+ end
1829
+ else
1830
+ puts "이미지 결과가 없어 다운로드할 수 없습니다. 단색 배경 이미지 생성합니다."
1831
+ color_image
1832
+ end
1833
+ end
1654
1834
  end
1655
1835
  end
1656
1836
 
@@ -1711,40 +1891,84 @@ class Wordpress
1711
1891
  img.write('./image/memory.png')
1712
1892
  end
1713
1893
 
1894
+ def wrap_text_to_fit(draw, text, max_width, max_height, font_path, initial_size)
1895
+ size = initial_size
1896
+ draw.font = font_path
1897
+
1898
+ loop do
1899
+ draw.pointsize = size
1900
+ words = text.chars # 글자 단위로 자름 (한국어 기준)
1901
+ lines = []
1902
+ line = ""
1903
+
1904
+ words.each do |char|
1905
+ test_line = line + char
1906
+ metrics = draw.get_type_metrics(test_line)
1907
+ if metrics.width > max_width
1908
+ lines << line
1909
+ line = char
1910
+ else
1911
+ line = test_line
1912
+ end
1913
+ end
1914
+ lines << line unless line.empty?
1915
+
1916
+ line_height = draw.get_type_metrics("가").height
1917
+ total_height = line_height * lines.size
1918
+
1919
+ # 세로 초과 안 하면 성공
1920
+ if total_height <= max_height || size <= 10
1921
+ return [lines.join("\n"), size]
1922
+ else
1923
+ size -= 2
1924
+ end
1925
+ end
1926
+ end
1927
+
1928
+
1714
1929
  def image_text(text1, text2)
1715
1930
  begin
1716
1931
  color = File.open('./color.ini', 'r', :encoding => 'utf-8').read().split("\n")
1717
- font = Dir.entries('./fonts')
1718
- img = Magick::Image.read('./image/memory.png').first
1719
- text = Magick::Draw.new
1932
+ font_files = Dir.entries('./fonts').select { |f| f.downcase.end_with?('.ttf') }
1933
+ font2 = './fonts/' + font_files.sample
1720
1934
  color2 = color.sample
1721
- font2 = './fonts/'+font.sample
1722
- message = text1.to_s+"\n"+text2.to_s
1935
+
1936
+ img = Magick::Image.read('./image/memory.png').first
1937
+ draw = Magick::Draw.new
1938
+
1939
+ raw_message = "#{text1}\n#{text2}".strip
1940
+ max_width = img.columns * 0.85
1941
+ max_height = img.rows * 0.6
1942
+
1723
1943
  begin
1724
1944
  size = rand(@data['이미지설정']['이미지글자1크기1'].text.to_i..@data['이미지설정']['이미지글자1크기2'].text.to_i)
1725
1945
  rescue
1726
1946
  size = 30
1727
1947
  end
1948
+
1949
+ wrapped_message, adjusted_size = wrap_text_to_fit(draw, raw_message, max_width, max_height, font2, size)
1950
+
1728
1951
  if @data['이미지설정']['글자그림자'].checked?
1729
- img.annotate(text, 0,0, +3,+3, message) do
1730
- text.gravity = Magick::CenterGravity
1731
- text.pointsize = size
1732
- text.fill = '#000000'
1733
- text.font = font2
1952
+ img.annotate(draw, 0, 0, 2, 2, wrapped_message) do
1953
+ draw.gravity = Magick::CenterGravity
1954
+ draw.pointsize = adjusted_size
1955
+ draw.fill = '#000000'
1956
+ draw.font = font2
1734
1957
  end
1735
1958
  end
1736
-
1737
- img.annotate(text, 0,0,0,0, message) do
1738
- text.gravity = Magick::CenterGravity
1739
- text.pointsize = size
1959
+
1960
+ draw2 = Magick::Draw.new
1961
+ img.annotate(draw2, 0, 0, 0, 0, wrapped_message) do
1962
+ draw2.gravity = Magick::CenterGravity
1963
+ draw2.pointsize = adjusted_size
1964
+ draw2.fill = color2
1965
+ draw2.font = font2
1740
1966
  if @data['이미지설정']['글자테두리'].checked?
1741
- text.stroke_width = 2
1742
- text.stroke = '#000000'
1967
+ draw2.stroke_width = 2
1968
+ draw2.stroke = '#000000'
1743
1969
  end
1744
- text.fill = color2
1745
- text.font = font2
1746
1970
  end
1747
-
1971
+
1748
1972
  img.write('./image/memory.png')
1749
1973
  rescue
1750
1974
  puts '이미지 폰트 불러오기 오류 재시도...'
@@ -1806,18 +2030,40 @@ class Wordpress
1806
2030
  image_filter()
1807
2031
  end
1808
2032
 
1809
- insert_image_text1 = ''
1810
- insert_image_text2 = ''
1811
2033
  if @data['이미지설정']['글자삽입1'].checked?
1812
- insert_image_text1 = @data['이미지설정']['이미지글자1'].sample
2034
+ if @data['이미지설정']['이미지글자1'].length == 0
2035
+ image_text_path1 = ''
2036
+ else
2037
+ if @data['이미지설정']['글자랜덤'].checked?
2038
+ image_text_path1 = @data['이미지설정']['이미지글자1'].sample
2039
+ else
2040
+ image_text_path1 = @data['이미지설정']['이미지글자1'][@image_text_soon1]
2041
+ @image_text_soon1 += 1
2042
+ if @image_text_soon1 > @data['이미지설정']['이미지글자1'].length - 1
2043
+ @image_text_soon1 = 0
2044
+ end
2045
+ end
2046
+ end
1813
2047
  end
1814
2048
 
1815
2049
  if @data['이미지설정']['글자삽입2'].checked?
1816
- insert_image_text2 = @data['이미지설정']['이미지글자2'].sample
2050
+ if @data['이미지설정']['이미지글자2'].length == 0
2051
+ image_text_path2 = ''
2052
+ else
2053
+ if @data['이미지설정']['글자랜덤'].checked?
2054
+ image_text_path2 = @data['이미지설정']['이미지글자2'].sample
2055
+ else
2056
+ image_text_path2 = @data['이미지설정']['이미지글자2'][@image_text_soon2]
2057
+ @image_text_soon2 += 1
2058
+ if @image_text_soon2 > @data['이미지설정']['이미지글자2'].length - 1
2059
+ @image_text_soon2 = 0
2060
+ end
2061
+ end
2062
+ end
1817
2063
  end
1818
-
2064
+
1819
2065
  if @data['이미지설정']['글자삽입1'].checked? or @data['이미지설정']['글자삽입2'].checked?
1820
- image_text(insert_image_text1, insert_image_text2)
2066
+ image_text(image_text_path1, image_text_path2)
1821
2067
  end
1822
2068
 
1823
2069
  if @data['이미지설정']['테두리사용'].checked?
@@ -1881,6 +2127,8 @@ class Wordpress
1881
2127
  title_soon = 0
1882
2128
  keyword_soon = 0
1883
2129
  content_soon = 0
2130
+ @image_text_soon1 = 0
2131
+ @image_text_soon2 = 0
1884
2132
  @my_ip = 'init'
1885
2133
  @image_counter = 0
1886
2134
  @inumber2 = 0
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tblog_duopack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.39
4
+ version: 0.0.51
5
5
  platform: ruby
6
6
  authors:
7
7
  - zon
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-12 00:00:00.000000000 Z
10
+ date: 2025-05-29 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: File to Clipboard gem
13
13
  email: mymin26@naver.com