File size: 5,359 Bytes
49ec9dc
 
2edcd19
49ec9dc
 
2edcd19
 
 
 
 
 
49ec9dc
 
 
 
2edcd19
49ec9dc
 
 
 
 
 
2edcd19
 
49ec9dc
 
 
 
 
2edcd19
49ec9dc
 
 
 
 
 
 
2edcd19
49ec9dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2edcd19
 
 
 
 
 
49ec9dc
 
 
 
 
 
 
 
 
 
 
 
2edcd19
 
 
 
49ec9dc
 
 
2edcd19
 
49ec9dc
2edcd19
49ec9dc
 
 
 
 
 
 
 
 
 
 
 
 
 
2edcd19
49ec9dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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 토큰
# message_content μΈν…νŠΈ μ‚¬μš© μ—¬λΆ€(1 = ON, 0 = OFF)
USE_MSG_INTENT = os.getenv("MESSAGE_CONTENT_INTENT", "1") != "0"

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 = (
    "bytedance/sdxl-lightning-4step:"
    "6f7a773af6fc3e8de9d5a3c00be77c17308914bf67772726aff83496ba1e3bbe"
)

# ── λ²ˆμ—­ νŒŒμ΄ν”„λΌμΈ (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()
if USE_MSG_INTENT:
    intents.message_content = True
    logging.info("Message Content Intent = ON  "
                 "(Discord Developer Portal β†’ Bot β†’ Privileged Intents μ—μ„œ ν™œμ„±ν™” ν•„μš”)")
else:
    logging.info("Message Content Intent = OFF  (MESSAGE_CONTENT_INTENT=0)")

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):
        # μΈν…νŠΈ OFF μ‹œ ν”„λ‘¬ν”„νŠΈλ₯Ό 읽을 수 μ—†μœΌλ―€λ‘œ μ¦‰μ‹œ λ°˜ν™˜
        if not USE_MSG_INTENT:
            return
        # 봇 μžμ‹ μ˜ λ©”μ‹œμ§€ λ˜λŠ” λ‹€λ₯Έ 채널 λ©”μ‹œμ§€ β†’ λ¬΄μ‹œ
        if message.author.id == self.user.id or message.channel.id != CHANNEL_ID:
            return

        prompt_raw = message.content.strip()
        if not prompt_raw:
            return
        prompt_en = ko2en(prompt_raw)

        await message.channel.typing()

        # ── Replicate 호좜 ──────────────────────────────────
        def run_replicate():
            return list(replicate.run(MODEL, input={"prompt": prompt_en}))

        try:
            images = 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 파일 μ—…λ‘œλ“œ ─────────────────────────────
        files = []
        for idx, item in enumerate(images):
            try:
                data = item.read() if hasattr(item, "read") else requests.get(item).content
                files.append(discord.File(io.BytesIO(data), filename=f"img_{idx}.png"))
            except Exception as e:
                logging.warning(f"[IMG {idx}] 처리 μ‹€νŒ¨: {e}")

        await message.reply(
            files=files if files else None,
            content=None if files else "⚠️ 이미지λ₯Ό 전솑할 수 μ—†μŠ΅λ‹ˆλ‹€."
        )

# ── μ‹€ν–‰ ────────────────────────────────────────────────────
if __name__ == "__main__":
    replicate.Client(api_token=REPL_TOKEN)  # Replicate 인증
    ImageBot(intents=intents).run(TOKEN)