fantos's picture
Update app.py
9e12771 verified
# img_bot.py
import discord, os, io, re, asyncio, logging, requests, replicate, subprocess
from transformers import pipeline as transformers_pipeline # λ²ˆμ—­ νŒŒμ΄ν”„λΌμΈ
# ── ν™˜κ²½ λ³€μˆ˜ ────────────────────────────────────────────────
TOKEN = os.getenv("DISCORD_TOKEN") # Discord 봇 토큰
CHANNEL_ID = int(os.getenv("DISCORD_CHANNEL_ID")) # κ°μ‹œν•  채널 ID
REPL_TOKEN = (os.getenv("OPENAI_API_KEY") or "").strip() # Replicate 토큰(동일 λ³€μˆ˜ μ‚¬μš©)
HF_TOKEN = (os.getenv("HF_TOKEN") or "").strip() # Hugging Face Personal Access Token
if not TOKEN or not CHANNEL_ID:
raise RuntimeError("DISCORD_TOKEN κ³Ό DISCORD_CHANNEL_ID ν™˜κ²½ λ³€μˆ˜λ₯Ό λͺ¨λ‘ μ§€μ •ν•˜μ„Έμš”.")
if not REPL_TOKEN:
raise RuntimeError(
"OPENAI_API_KEY ν™˜κ²½ λ³€μˆ˜μ— Replicate Personal Access Token 값을 λ„£μ–΄μ£Όμ„Έμš”."
)
# Replicate λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μ°Έμ‘°ν•˜λ„λ‘ 토큰 μ£Όμž…
os.environ["REPLICATE_API_TOKEN"] = REPL_TOKEN
# ── λͺ¨λΈ ────────────────────────────────────────────────────
MODEL = "luma/ray-flash-2-540p" # ν…μŠ€νŠΈ-투-λΉ„λ””μ˜€ λͺ¨λΈ (540p)
# ── λ²ˆμ—­ νŒŒμ΄ν”„λΌμΈ (CPU) ───────────────────────────────────
translator_kwargs = {"device": -1}
if HF_TOKEN:
translator_kwargs["token"] = HF_TOKEN # 인증 토큰 전달
translator = transformers_pipeline(
"translation",
model="Helsinki-NLP/opus-mt-ko-en",
**translator_kwargs
)
def ko2en(text: str) -> str:
"""ν•œκΈ€ 포함 μ‹œ μ˜μ–΄ λ²ˆμ—­, κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ 원문 λ°˜ν™˜."""
if re.search(r"[κ°€-힣]", text):
try:
return translator(text, max_length=512)[0]["translation_text"].strip()
except Exception as e:
logging.warning(f"λ²ˆμ—­ μ‹€νŒ¨, 원문 μ‚¬μš©: {e}")
return text
# ── λ‘œκΉ… ────────────────────────────────────────────────────
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[logging.StreamHandler()]
)
# ── Discord μ„€μ • ────────────────────────────────────────────
intents = discord.Intents.default()
intents.message_content = True # λ©”μ‹œμ§€ μ½˜ν…μΈ  읽기
class ImageBot(discord.Client): # 이름 μœ μ§€
async def on_ready(self):
logging.info(f"Logged in as {self.user} (id={self.user.id})")
# web.py 병렬 μ‹€ν–‰
try:
subprocess.Popen(["python", "web.py"])
logging.info("web.py server has been started.")
except Exception as e:
logging.warning(f"web.py μ‹€ν–‰ μ‹€νŒ¨: {e}")
async def on_message(self, message: discord.Message):
# 봇 μžμ‹ μ˜ λ©”μ‹œμ§€ ν˜Ήμ€ λŒ€μƒ μ•„λ‹Œ 채널 β†’ λ¬΄μ‹œ
if message.author.id == self.user.id or message.channel.id != CHANNEL_ID:
return
prompt = message.content.strip()
if not prompt:
return
prompt_en = ko2en(prompt) # ν•œκΈ€μ΄λ©΄ μ˜μ–΄λ‘œ λ³€ν™˜
await message.channel.typing()
# ── Replicate 호좜 ──────────────────────────────────
def run_replicate():
# ν•„μš”ν•œ 경우 input λ”•μ…”λ„ˆλ¦¬μ— duration, seed λ“± μΆ”κ°€ κ°€λŠ₯
return replicate.run(MODEL, input={"prompt": prompt_en})
try:
output = await asyncio.get_running_loop().run_in_executor(None, run_replicate)
except Exception as e:
logging.error(f"Replicate error: {e}")
await message.reply("⚠️ λΉ„λ””μ˜€ 생성 μ‹€νŒ¨!")
return
# ── λΉ„λ””μ˜€ Discord 전솑 ─────────────────────────────
try:
# replicate.run κ²°κ³Όκ°€ list/tuple 이면 첫 번째 μš”μ†Œ μ‚¬μš©
if isinstance(output, (list, tuple)):
output = output[0]
# URL(str) β†’ λ‹€μš΄λ‘œλ“œ, bytes/file-like β†’ κ·ΈλŒ€λ‘œ
if isinstance(output, str):
data = requests.get(output).content
else:
data = output.read() if hasattr(output, "read") else output
video_file = discord.File(io.BytesIO(data), filename="output.mp4")
await message.reply(files=[video_file])
except Exception as e:
logging.warning(f"λΉ„λ””μ˜€ 처리 μ‹€νŒ¨: {e}")
await message.reply("⚠️ λΉ„λ””μ˜€λ₯Ό 전솑할 수 μ—†μŠ΅λ‹ˆλ‹€.")
# ── μ‹€ν–‰ ────────────────────────────────────────────────────
if __name__ == "__main__":
replicate.Client(api_token=REPL_TOKEN) # Replicate 인증
ImageBot(intents=intents).run(TOKEN)