File size: 7,703 Bytes
c48780b
 
 
c3a9e90
2cfdf65
c3a9e90
c48780b
 
 
7b7fd88
b0940a1
c48780b
 
 
 
 
 
49ec9dc
c3a9e90
444fc48
 
 
 
49ec9dc
 
 
 
7b7fd88
c48780b
 
49ec9dc
c3a9e90
 
 
49ec9dc
c3a9e90
49ec9dc
7b7fd88
 
 
 
49ec9dc
 
444fc48
c48780b
444fc48
 
 
 
c80bb75
444fc48
 
 
 
 
 
49ec9dc
c3a9e90
 
 
2cfdf65
c3a9e90
 
49ec9dc
c3a9e90
49ec9dc
b0940a1
49ec9dc
 
 
 
 
 
 
 
 
 
 
 
 
7b7fd88
2edcd19
 
49ec9dc
 
c80bb75
49ec9dc
 
c3a9e90
6659834
 
444fc48
c80bb75
b6dfa24
b0940a1
 
b6dfa24
444fc48
6659834
c3a9e90
49ec9dc
 
c3a9e90
49ec9dc
6659834
49ec9dc
 
 
c48780b
 
c3a9e90
 
2473562
c3a9e90
c48780b
 
2473562
c48780b
 
 
 
b08146f
 
c3a9e90
c48780b
 
 
 
 
 
 
 
 
 
2473562
c48780b
c3a9e90
b08146f
 
2473562
b08146f
2473562
 
 
 
 
 
 
 
 
c48780b
b08146f
c48780b
 
 
2473562
 
 
 
b08146f
 
2473562
c48780b
2473562
 
 
c3a9e90
2473562
b08146f
2473562
 
c3a9e90
2473562
c3a9e90
c48780b
 
 
 
 
 
 
 
 
 
 
 
 
c3a9e90
49ec9dc
c48780b
 
 
 
 
c3a9e90
 
49ec9dc
c48780b
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# img_bot.py
# β€œμ΄λ―Έμ§€ 첨뢀 β†’ μ‹€νŒ¨ μ‹œ URL 좜λ ₯” μ™„κ²°νŒ

import os, io, re, random, asyncio, logging, subprocess, base64
from urllib.parse import urljoin, quote_plus

import discord
import requests
import replicate
from transformers import pipeline as transformers_pipeline
from gradio_client import Client

try:
    from PIL import Image                # WEBP β†’ PNG λ³€ν™˜
    PIL_OK = True
except Exception:
    PIL_OK = False                       # Pillow λ―Έμ„€μΉ˜ μ‹œ λ³€ν™˜ μƒλž΅

# ────────────────── ν™˜κ²½ λ³€μˆ˜ ──────────────────
TOKEN       = os.getenv("DISCORD_TOKEN")
CHANNEL_ID  = int(os.getenv("DISCORD_CHANNEL_ID"))
REPL_TOKEN  = (os.getenv("OPENAI_API_KEY") or "").strip()
HF_TOKEN    = (os.getenv("HF_TOKEN") or "").strip()

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 값을 λ„£μ–΄μ£Όμ„Έμš”.")

os.environ["REPLICATE_API_TOKEN"] = REPL_TOKEN          # (Replicate λ―Έμ‚¬μš©)

# ────────────────── Gradio μ„œλ²„ ─────────────────
GRADIO_URL = "http://211.233.58.201:7971"
GRADIO_API = "/process_and_save_image"

# ────────────────── λ²ˆμ—­ νŒŒμ΄ν”„λΌμΈ ─────────────
translator = transformers_pipeline(
    "translation",
    model="Helsinki-NLP/opus-mt-ko-en",
    device=-1,
    **({"token": HF_TOKEN} if HF_TOKEN else {})
)

async def ko2en_async(text: str) -> str:
    """ν•œκΈ€ 포함 μ‹œ μ˜μ–΄ λ²ˆμ—­."""
    if not re.search(r"[κ°€-힣]", text):
        return text
    loop = asyncio.get_running_loop()
    try:
        return await loop.run_in_executor(
            None,
            lambda: translator(text, max_length=256, num_beams=1)[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})")
        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_raw = message.content.strip()
        if not prompt_raw:
            return

        prompt_en = await ko2en_async(prompt_raw)
        await message.channel.typing()

        # ───────────── Gradio 호좜 ──────────────
        def generate_image():
            client = Client(GRADIO_URL)
            return client.predict(
                height=768,
                width=768,
                steps=30,
                scales=3.5,
                prompt=prompt_en,
                seed=random.randint(0, 2**32 - 1),
                api_name=GRADIO_API
            )

        try:
            result = await asyncio.get_running_loop().run_in_executor(None, generate_image)
        except Exception as e:
            logging.error(f"Gradio API error: {e}")
            await message.reply("⚠️ 이미지 생성 μ‹€νŒ¨!")
            return

        # 리슀트 κ²°κ³Ό β†’ 첫 μš”μ†Œ
        if isinstance(result, list):
            result = result[0]

        # ───────────── 이미지 데이터 확보 ─────────────
        data = None
        remote_path = None               # μ‹€νŒ¨ μ‹œ μ‚¬μš©μžμ—κ²Œ 보여쀄 URL
        candidate_urls: list[str] = []

        def add_remote(p: str):
            url = urljoin(GRADIO_URL, f"/gradio_api/file={quote_plus(p)}")
            candidate_urls.append(url)
            return url

        # dict κ²°κ³Ό
        if isinstance(result, dict):
            u = result.get("url")
            if u:
                if u.startswith("/"):
                    u = urljoin(GRADIO_URL, u)
                candidate_urls.append(u)
                remote_path = u
            p = result.get("path")
            if p:
                remote_path = add_remote(p)
                if os.path.isfile(p):
                    try:
                        with open(p, "rb") as f:
                            data = f.read()
                    except Exception:
                        pass

        # str κ²°κ³Ό
        elif isinstance(result, str):
            s = result.strip()
            if s.startswith("data:"):
                try:
                    data = base64.b64decode(re.sub(r"^data:image/[^;]+;base64,", "", s))
                except Exception as e:
                    logging.warning(f"base64 λ””μ½”λ”© μ‹€νŒ¨: {e}")
            elif s.startswith("http"):
                candidate_urls.append(s)
                remote_path = s
            else:
                remote_path = add_remote(s)
                if s.startswith("/"):
                    candidate_urls.append(urljoin(GRADIO_URL, s))
                if os.path.isfile(s):
                    try:
                        with open(s, "rb") as f:
                            data = f.read()
                    except Exception:
                        pass

        # URL λ‹€μš΄λ‘œλ“œ (헀더 λ¬΄μ‹œ, 200 OK 만 확인)
        for u in candidate_urls:
            if data:
                break
            try:
                r = requests.get(u, timeout=10)
                if r.ok and r.content:
                    data = r.content
                    break
            except Exception as e:
                logging.warning(f"URL λ‹€μš΄λ‘œλ“œ μ‹€νŒ¨({u}): {e}")

        # ───────────── Discord 전솑 ─────────────
        if data:
            if PIL_OK:  # WEBP β†’ PNG λ³€ν™˜
                try:
                    buf = io.BytesIO()
                    Image.open(io.BytesIO(data)).convert("RGB").save(buf, format="PNG")
                    buf.seek(0)
                    await message.reply(files=[discord.File(buf, filename="image.png")])
                    return
                except Exception as e:
                    logging.warning(f"PNG λ³€ν™˜ μ‹€νŒ¨: {e}")
            # λ³€ν™˜ μ‹€νŒ¨ λ˜λŠ” Pillow μ—†μŒ β†’ 원본 전솑
            await message.reply(files=[discord.File(io.BytesIO(data), filename="image.webp")])
            return

        # ───────────── μ‹€νŒ¨ μ‹œ URL 좜λ ₯ ─────────────
        if remote_path:
            await message.reply(content=remote_path)
        else:
            await message.reply("⚠️ 이미지λ₯Ό 전솑할 수 μ—†μŠ΅λ‹ˆλ‹€.")

# ────────────────── μ‹€ν–‰ ────────────────────────
if __name__ == "__main__":
    replicate.Client(api_token=REPL_TOKEN)   # ꡬ쑰 μœ μ§€
    ImageBot(intents=intents).run(TOKEN)