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.
- checksums.yaml +4 -4
- data/lib/tblog_duopack.rb +303 -55
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad9d079f87010d2cb76d806d622812ff121b447c50a53281c5405638a4ec8b3c
|
4
|
+
data.tar.gz: d272c07dac84708c42baf4a2130c3c119ef81cc314ceeb6a0b5f3437db35beb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
245
|
-
|
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("
|
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
|
-
|
384
|
-
|
385
|
-
|
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
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
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
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
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
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
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
|
-
|
1718
|
-
|
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
|
-
|
1722
|
-
|
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(
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
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
|
-
|
1738
|
-
|
1739
|
-
|
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
|
-
|
1742
|
-
|
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
|
-
|
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
|
-
|
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(
|
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.
|
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-
|
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
|