Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -1,10 +1,15 @@
|
|
1 |
# img_bot.py
|
2 |
-
import
|
3 |
from urllib.parse import urljoin, quote_plus
|
|
|
|
|
|
|
|
|
4 |
from transformers import pipeline as transformers_pipeline
|
5 |
from gradio_client import Client
|
|
|
6 |
|
7 |
-
#
|
8 |
TOKEN = os.getenv("DISCORD_TOKEN")
|
9 |
CHANNEL_ID = int(os.getenv("DISCORD_CHANNEL_ID"))
|
10 |
REPL_TOKEN = (os.getenv("OPENAI_API_KEY") or "").strip()
|
@@ -14,14 +19,13 @@ if not TOKEN or not CHANNEL_ID:
|
|
14 |
raise RuntimeError("DISCORD_TOKEN 과 DISCORD_CHANNEL_ID 환경 변수를 모두 지정하세요.")
|
15 |
if not REPL_TOKEN:
|
16 |
raise RuntimeError("OPENAI_API_KEY 에 Replicate Personal Access Token 값을 넣어주세요.")
|
|
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
GRADIO_URL = "http://211.233.58.201:7971"
|
22 |
-
GRADIO_API = "/process_and_save_image"
|
23 |
|
24 |
-
#
|
25 |
translator = transformers_pipeline(
|
26 |
"translation",
|
27 |
model="Helsinki-NLP/opus-mt-ko-en",
|
@@ -30,7 +34,7 @@ translator = transformers_pipeline(
|
|
30 |
)
|
31 |
|
32 |
async def ko2en_async(text: str) -> str:
|
33 |
-
"""한글 포함 시
|
34 |
if not re.search(r"[가-힣]", text):
|
35 |
return text
|
36 |
loop = asyncio.get_running_loop()
|
@@ -43,12 +47,14 @@ async def ko2en_async(text: str) -> str:
|
|
43 |
logging.warning(f"번역 실패, 원문 사용: {e}")
|
44 |
return text
|
45 |
|
46 |
-
#
|
47 |
-
logging.basicConfig(
|
|
|
48 |
format="%(asctime)s [%(levelname)s] %(message)s",
|
49 |
-
handlers=[logging.StreamHandler()]
|
|
|
50 |
|
51 |
-
#
|
52 |
intents = discord.Intents.default()
|
53 |
intents.message_content = True
|
54 |
|
@@ -72,7 +78,7 @@ class ImageBot(discord.Client):
|
|
72 |
prompt_en = await ko2en_async(prompt_raw)
|
73 |
await message.channel.typing()
|
74 |
|
75 |
-
#
|
76 |
def generate_image():
|
77 |
client = Client(GRADIO_URL)
|
78 |
return client.predict(
|
@@ -83,75 +89,69 @@ class ImageBot(discord.Client):
|
|
83 |
prompt=prompt_en,
|
84 |
seed=random.randint(0, 2**32 - 1),
|
85 |
api_name=GRADIO_API
|
86 |
-
)
|
87 |
|
88 |
try:
|
89 |
-
|
90 |
-
logging.info(f"Gradio result type={type(img_info)} → {img_info}")
|
91 |
except Exception as e:
|
92 |
logging.error(f"Gradio API error: {e}")
|
93 |
await message.reply("⚠️ 이미지 생성 실패!")
|
94 |
return
|
95 |
|
96 |
-
#
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
if isinstance(img_info, list) and img_info:
|
109 |
-
img_info = img_info[0]
|
110 |
-
|
111 |
-
# ② dict
|
112 |
-
if isinstance(img_info, dict):
|
113 |
-
# url 우선
|
114 |
-
url = img_info.get("url")
|
115 |
-
if url:
|
116 |
-
if url.startswith("data:"):
|
117 |
-
try:
|
118 |
-
data = base64.b64decode(re.sub(r"^data:image/[^;]+;base64,", "", url))
|
119 |
-
except Exception as e:
|
120 |
-
logging.warning(f"base64 디코딩 실패: {e}")
|
121 |
-
else:
|
122 |
-
if url.startswith("/"):
|
123 |
-
url = urljoin(GRADIO_URL, url)
|
124 |
-
data = download(url)
|
125 |
-
# path 보조
|
126 |
-
if data is None:
|
127 |
-
path = img_info.get("path")
|
128 |
-
if path:
|
129 |
-
# 원격 Gradio 파일 end-point
|
130 |
-
remote_url = urljoin(GRADIO_URL, f"/gradio_api/file={quote_plus(path)}")
|
131 |
-
data = download(remote_url) or (open(path, "rb").read() if os.path.isfile(path) else None)
|
132 |
-
|
133 |
-
# ③ str (로컬 경로/URL/data URI)
|
134 |
-
elif isinstance(img_info, str):
|
135 |
-
s = img_info.strip()
|
136 |
-
if s.startswith("data:"):
|
137 |
try:
|
138 |
-
data =
|
139 |
-
except Exception
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
await message.reply("⚠️ 이미지를 전송할 수 없습니다.")
|
|
|
153 |
|
154 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
if __name__ == "__main__":
|
156 |
replicate.Client(api_token=REPL_TOKEN) # 구조 유지
|
157 |
ImageBot(intents=intents).run(TOKEN)
|
|
|
1 |
# img_bot.py
|
2 |
+
import os, io, re, random, asyncio, logging, subprocess, base64
|
3 |
from urllib.parse import urljoin, quote_plus
|
4 |
+
|
5 |
+
import discord
|
6 |
+
import requests
|
7 |
+
import replicate
|
8 |
from transformers import pipeline as transformers_pipeline
|
9 |
from gradio_client import Client
|
10 |
+
from PIL import Image # WEBP → PNG 변환용
|
11 |
|
12 |
+
# ────────────────── 환경 변수 ──────────────────
|
13 |
TOKEN = os.getenv("DISCORD_TOKEN")
|
14 |
CHANNEL_ID = int(os.getenv("DISCORD_CHANNEL_ID"))
|
15 |
REPL_TOKEN = (os.getenv("OPENAI_API_KEY") or "").strip()
|
|
|
19 |
raise RuntimeError("DISCORD_TOKEN 과 DISCORD_CHANNEL_ID 환경 변수를 모두 지정하세요.")
|
20 |
if not REPL_TOKEN:
|
21 |
raise RuntimeError("OPENAI_API_KEY 에 Replicate Personal Access Token 값을 넣어주세요.")
|
22 |
+
os.environ["REPLICATE_API_TOKEN"] = REPL_TOKEN # Replicate 미사용이지만 구조 유지
|
23 |
|
24 |
+
# ────────────────── Gradio 서버 ─────────────────
|
25 |
+
GRADIO_URL = "http://211.233.58.201:7971"
|
26 |
+
GRADIO_API = "/process_and_save_image"
|
|
|
|
|
27 |
|
28 |
+
# ────────────────── 번역 파이프라인 ─────────────
|
29 |
translator = transformers_pipeline(
|
30 |
"translation",
|
31 |
model="Helsinki-NLP/opus-mt-ko-en",
|
|
|
34 |
)
|
35 |
|
36 |
async def ko2en_async(text: str) -> str:
|
37 |
+
"""한글 포함 시 영어 번역(비동기)."""
|
38 |
if not re.search(r"[가-힣]", text):
|
39 |
return text
|
40 |
loop = asyncio.get_running_loop()
|
|
|
47 |
logging.warning(f"번역 실패, 원문 사용: {e}")
|
48 |
return text
|
49 |
|
50 |
+
# ────────────────── 로깅 ────────────────────────
|
51 |
+
logging.basicConfig(
|
52 |
+
level=logging.INFO,
|
53 |
format="%(asctime)s [%(levelname)s] %(message)s",
|
54 |
+
handlers=[logging.StreamHandler()]
|
55 |
+
)
|
56 |
|
57 |
+
# ────────────────── Discord 클라이언트 ──────────
|
58 |
intents = discord.Intents.default()
|
59 |
intents.message_content = True
|
60 |
|
|
|
78 |
prompt_en = await ko2en_async(prompt_raw)
|
79 |
await message.channel.typing()
|
80 |
|
81 |
+
# ───────────── Gradio 호출 ──────────────
|
82 |
def generate_image():
|
83 |
client = Client(GRADIO_URL)
|
84 |
return client.predict(
|
|
|
89 |
prompt=prompt_en,
|
90 |
seed=random.randint(0, 2**32 - 1),
|
91 |
api_name=GRADIO_API
|
92 |
+
)
|
93 |
|
94 |
try:
|
95 |
+
result = await asyncio.get_running_loop().run_in_executor(None, generate_image)
|
|
|
96 |
except Exception as e:
|
97 |
logging.error(f"Gradio API error: {e}")
|
98 |
await message.reply("⚠️ 이미지 생성 실패!")
|
99 |
return
|
100 |
|
101 |
+
# Gradio 결과 → list / dict / str 중 첫 요소로 정규화
|
102 |
+
if isinstance(result, list):
|
103 |
+
result = result[0]
|
104 |
+
|
105 |
+
# ────────────��� URL 있으면 링크로 전송 ─────────────
|
106 |
+
url = None
|
107 |
+
if isinstance(result, dict):
|
108 |
+
url = result.get("url")
|
109 |
+
if url and url.startswith("/"):
|
110 |
+
url = urljoin(GRADIO_URL, url)
|
111 |
+
elif isinstance(result, str) and result.startswith(("http", "/")):
|
112 |
+
url = result if result.startswith("http") else urljoin(GRADIO_URL, result)
|
113 |
+
|
114 |
+
if url:
|
115 |
+
await message.reply(content=url) # Discord가 embed 미리보기
|
116 |
+
return
|
117 |
|
118 |
+
# ───────────── 첨부 파일 준비 ─────────────
|
119 |
+
data = None
|
120 |
+
# dict → path
|
121 |
+
if isinstance(result, dict):
|
122 |
+
path = result.get("path")
|
123 |
+
if path:
|
124 |
+
# Gradio 파일 다운로드 우선
|
125 |
+
remote = urljoin(GRADIO_URL, f"/gradio_api/file={quote_plus(path)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
try:
|
127 |
+
data = requests.get(remote, timeout=10).content
|
128 |
+
except Exception:
|
129 |
+
if os.path.isfile(path):
|
130 |
+
with open(path, "rb") as f:
|
131 |
+
data = f.read()
|
132 |
+
# str → data URI
|
133 |
+
elif isinstance(result, str) and result.startswith("data:"):
|
134 |
+
try:
|
135 |
+
b64 = re.sub(r"^data:image/[^;]+;base64,", "", result)
|
136 |
+
data = base64.b64decode(b64)
|
137 |
+
except Exception as e:
|
138 |
+
logging.warning(f"base64 디코딩 실패: {e}")
|
139 |
+
|
140 |
+
if not data:
|
141 |
await message.reply("⚠️ 이미지를 전송할 수 없습니다.")
|
142 |
+
return
|
143 |
|
144 |
+
# ───────────── WEBP → PNG 변환 (미리보기용) ─────────────
|
145 |
+
buf = io.BytesIO()
|
146 |
+
try:
|
147 |
+
Image.open(io.BytesIO(data)).convert("RGB").save(buf, format="PNG")
|
148 |
+
buf.seek(0)
|
149 |
+
await message.reply(files=[discord.File(buf, filename="image.png")])
|
150 |
+
except Exception:
|
151 |
+
# 변환 실패 시 원본 그대로
|
152 |
+
await message.reply(files=[discord.File(io.BytesIO(data), filename="image.webp")])
|
153 |
+
|
154 |
+
# ────────────────── 실행 ────────────────────────
|
155 |
if __name__ == "__main__":
|
156 |
replicate.Client(api_token=REPL_TOKEN) # 구조 유지
|
157 |
ImageBot(intents=intents).run(TOKEN)
|