Manggomee commited on
Commit
d0a7e74
ยท
verified ยท
1 Parent(s): 20308f4

Upload 4 files

Browse files
Files changed (3) hide show
  1. app.py +152 -32
  2. config.py +69 -0
  3. utils.py +180 -0
app.py CHANGED
@@ -1,38 +1,158 @@
1
- from transformers import pipeline
2
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
- # 1. pre-trained ๋ชจ๋ธ ๋กœ๋“œ(์‚ฌ์ „ํ•™์Šต๋œ Question Answering ํŒŒ์ดํ”„๋ผ์ธ)
5
- qna = pipeline("question-answering")
 
 
 
 
 
 
 
 
 
 
6
 
7
- def answer_question(question, context):
8
- result = qna({
9
- "question": question,
10
- "context": context
11
- })
12
- # ๊ฒฐ๊ณผ๊ฐ€ ๋ฆฌ์ŠคํŠธ๋ฉด ์ฒซ ๋‹ต๋ณ€๋งŒ, ์•„๋‹ˆ๋ฉด answer๊ฐ’ ๋ฐ˜ํ™˜
13
- if isinstance(result, list):
14
- return result[0]['answer']
15
- return result['answer']
16
 
17
- # 2. Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ (์ž…๋ ฅ์ฐฝ์— ์˜ˆ์‹œ๊นŒ์ง€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋„ฃ๊ธฐ)
18
- demo = gr.Interface(
19
- fn=answer_question,
20
- inputs=[
21
- gr.Textbox(
22
- lines=2,
23
- label="์งˆ๋ฌธ (Question)",
24
- value="๋Œ€ํ•œ๋ฏผ๊ตญ์˜ ์ˆ˜๋„๋Š” ์–ด๋””์ž…๋‹ˆ๊นŒ?" # โ˜… ์งˆ๋ฌธ ์˜ˆ์‹œ
25
- ),
26
- gr.Textbox(
27
- lines=6,
28
- label="๋ฌธ๋งฅ (Context)",
29
- value="๋Œ€ํ•œ๋ฏผ๊ตญ์€ ๋™์•„์‹œ์•„์— ์œ„์น˜ํ•œ ๊ตญ๊ฐ€๋กœ, ์ˆ˜๋„๋Š” ์„œ์šธ์ด๋‹ค. ์„œ์šธ์€ ์ •์น˜, ๊ฒฝ์ œ, ๋ฌธํ™”์˜ ์ค‘์‹ฌ์ง€๋กœ ์•ฝ ์ฒœ๋งŒ ๋ช…์ด ๊ฑฐ์ฃผํ•œ๋‹ค." # โ˜… ๋ฌธ๋งฅ/์ง€๋ฌธ ์˜ˆ์‹œ
30
- )
31
- ],
32
- outputs=gr.Textbox(label="๋‹ต๋ณ€ (Answer)"),
33
- title="์งˆ์˜์‘๋‹ต(Q&A) ์ฑ—๋ด‡",
34
- description="์งˆ๋ฌธ(Question)๊ณผ ๋ฌธ๋งฅ(์ง€๋ฌธ/Context)์„ ์ž…๋ ฅํ•˜๋ฉด AI๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋‹ต๋ณ€ํ•ด์ค๋‹ˆ๋‹ค."
35
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  if __name__ == "__main__":
38
- demo.launch()
 
 
 
 
 
 
1
  import gradio as gr
2
+ import random
3
+ import requests
4
+ from PIL import Image, ImageDraw, ImageFont
5
+ import io
6
+ import base64
7
+ from transformers import pipeline
8
+ from config import MEME_TEMPLATES, CHATBOT_RESPONSES, APP_CONFIG
9
+ from utils import create_meme_image, generate_response, add_text_to_image
10
+
11
+ # AI ๋ชจ๋ธ ์ดˆ๊ธฐํ™”
12
+ try:
13
+ qa_pipeline = pipeline("question-answering", model="distilbert-base-cased-distilled-squad")
14
+ text_generator = pipeline("text-generation", model="gpt2", max_length=100)
15
+ except Exception as e:
16
+ print(f"๋ชจ๋ธ ๋กœ๋”ฉ ์˜ค๋ฅ˜: {e}")
17
+ qa_pipeline = None
18
+ text_generator = None
19
+
20
+ def create_meme(template_choice, top_text, bottom_text):
21
+ """๋ฐˆ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ•จ์ˆ˜"""
22
+ try:
23
+ if template_choice in MEME_TEMPLATES:
24
+ template_info = MEME_TEMPLATES[template_choice]
25
+ # ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (์‹ค์ œ๋กœ๋Š” ๋ฏธ๋ฆฌ ์ค€๋น„๋œ ์ด๋ฏธ์ง€ ์‚ฌ์šฉ)
26
+ img = create_meme_image(template_info, top_text, bottom_text)
27
+ return img
28
+ else:
29
+ return "ํ…œํ”Œ๋ฆฟ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”."
30
+ except Exception as e:
31
+ return f"๋ฐˆ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
32
 
33
+ def chatbot_response(message, history):
34
+ """์ฑ—๋ด‡ ์‘๋‹ต ์ƒ์„ฑ"""
35
+ try:
36
+ if not message.strip():
37
+ return "๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
38
+
39
+ # ๊ฐ„๋‹จํ•œ ํ‚ค์›Œ๋“œ ๊ธฐ๋ฐ˜ ์‘๋‹ต
40
+ response = generate_response(message, qa_pipeline, text_generator)
41
+ return response
42
+
43
+ except Exception as e:
44
+ return f"์‘๋‹ต ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
45
 
46
+ def analyze_meme_trend(keyword):
47
+ """๋ฐˆ ํŠธ๋ Œ๋“œ ๋ถ„์„ (์‹œ๋ฎฌ๋ ˆ์ด์…˜)"""
48
+ trends = [
49
+ f"'{keyword}' ๊ด€๋ จ ๋ฐˆ์ด ์ตœ๊ทผ {random.randint(10, 50)}% ์ฆ๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.",
50
+ f"'{keyword}' ๋ฐˆ์˜ ์ธ๊ธฐ๋„: {random.randint(70, 95)}/100",
51
+ f"์œ ์‚ฌํ•œ ํ‚ค์›Œ๋“œ: {', '.join(random.sample(['์žฌ๋ฏธ์žˆ๋Š”', '์›ƒ๊ธด', '์ธ๊ธฐ', '๋ฐ”์ด๋Ÿด', 'ํŠธ๋ Œ๋“œ'], 3))}"
52
+ ]
53
+ return "\n".join(trends)
 
54
 
55
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ
56
+ with gr.Blocks(title=APP_CONFIG["title"], theme=gr.themes.Soft()) as demo:
57
+ gr.Markdown("# ๐ŸŽญ ๋ฐˆ ์ƒ์„ฑ & AI ์ฑ—๋ด‡ ์„œ๋น„์Šค")
58
+ gr.Markdown("๋ฐˆ์„ ๋งŒ๋“ค๊ณ , AI์™€ ๋Œ€ํ™”ํ•˜๋ฉฐ, ํŠธ๋ Œ๋“œ๋ฅผ ๋ถ„์„ํ•ด๋ณด์„ธ์š”!")
59
+
60
+ with gr.Tabs():
61
+ # ๋ฐˆ ์ƒ์„ฑ ํƒญ
62
+ with gr.TabItem("๐Ÿ–ผ๏ธ ๋ฐˆ ์ƒ์„ฑ๊ธฐ"):
63
+ with gr.Row():
64
+ with gr.Column():
65
+ template_dropdown = gr.Dropdown(
66
+ choices=list(MEME_TEMPLATES.keys()),
67
+ label="๋ฐˆ ํ…œํ”Œ๋ฆฟ ์„ ํƒ",
68
+ value=list(MEME_TEMPLATES.keys())[0]
69
+ )
70
+ top_text_input = gr.Textbox(
71
+ label="์ƒ๋‹จ ํ…์ŠคํŠธ",
72
+ placeholder="์ƒ๋‹จ์— ๋“ค์–ด๊ฐˆ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”",
73
+ value="When you see a bug"
74
+ )
75
+ bottom_text_input = gr.Textbox(
76
+ label="ํ•˜๋‹จ ํ…์ŠคํŠธ",
77
+ placeholder="ํ•˜๋‹จ์— ๋“ค์–ด๊ฐˆ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”",
78
+ value="But it works in production"
79
+ )
80
+ create_btn = gr.Button("๋ฐˆ ์ƒ์„ฑํ•˜๊ธฐ", variant="primary")
81
+
82
+ with gr.Column():
83
+ meme_output = gr.Image(label="์ƒ์„ฑ๋œ ๋ฐˆ")
84
+
85
+ create_btn.click(
86
+ fn=create_meme,
87
+ inputs=[template_dropdown, top_text_input, bottom_text_input],
88
+ outputs=meme_output
89
+ )
90
+
91
+ # AI ์ฑ—๋ด‡ ํƒญ
92
+ with gr.TabItem("๐Ÿค– AI ์ฑ—๋ด‡"):
93
+ chatbot = gr.Chatbot(
94
+ label="AI ์–ด์‹œ์Šคํ„ดํŠธ",
95
+ placeholder="์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฌด์—‡์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”?",
96
+ height=400
97
+ )
98
+ msg_input = gr.Textbox(
99
+ label="๋ฉ”์‹œ์ง€ ์ž…๋ ฅ",
100
+ placeholder="๊ถ๊ธˆํ•œ ๊ฒƒ์„ ๋ฌผ์–ด๋ณด์„ธ์š”...",
101
+ lines=2
102
+ )
103
+
104
+ with gr.Row():
105
+ send_btn = gr.Button("์ „์†ก", variant="primary")
106
+ clear_btn = gr.Button("๋Œ€ํ™” ์ดˆ๊ธฐํ™”")
107
+
108
+ def user_message(message, history):
109
+ return "", history + [[message, None]]
110
+
111
+ def bot_message(history):
112
+ if history and history[-1][1] is None:
113
+ user_msg = history[-1][0]
114
+ bot_response = chatbot_response(user_msg, history)
115
+ history[-1][1] = bot_response
116
+ return history
117
+
118
+ msg_input.submit(user_message, [msg_input, chatbot], [msg_input, chatbot]).then(
119
+ bot_message, chatbot, chatbot
120
+ )
121
+ send_btn.click(user_message, [msg_input, chatbot], [msg_input, chatbot]).then(
122
+ bot_message, chatbot, chatbot
123
+ )
124
+ clear_btn.click(lambda: [], outputs=chatbot)
125
+
126
+ # ํŠธ๋ Œ๋“œ ๋ถ„์„ ํƒญ
127
+ with gr.TabItem("๐Ÿ“ˆ ํŠธ๋ Œ๋“œ ๋ถ„์„"):
128
+ with gr.Row():
129
+ with gr.Column():
130
+ keyword_input = gr.Textbox(
131
+ label="ํ‚ค์›Œ๋“œ ์ž…๋ ฅ",
132
+ placeholder="๋ถ„์„ํ•  ํ‚ค์›Œ๋“œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”",
133
+ value="AI"
134
+ )
135
+ analyze_btn = gr.Button("ํŠธ๋ Œ๋“œ ๋ถ„์„", variant="primary")
136
+
137
+ with gr.Column():
138
+ trend_output = gr.Textbox(
139
+ label="๋ถ„์„ ๊ฒฐ๊ณผ",
140
+ lines=5,
141
+ interactive=False
142
+ )
143
+
144
+ analyze_btn.click(
145
+ fn=analyze_meme_trend,
146
+ inputs=keyword_input,
147
+ outputs=trend_output
148
+ )
149
+
150
+ gr.Markdown("---")
151
+ gr.Markdown("๐Ÿ’ก **์‚ฌ์šฉ๋ฒ•**: ๊ฐ ํƒญ์„ ํด๋ฆญํ•˜์—ฌ ๋ฐˆ ์ƒ์„ฑ, AI ์ฑ„ํŒ…, ํŠธ๋ Œ๋“œ ๋ถ„์„ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜์„ธ์š”!")
152
 
153
  if __name__ == "__main__":
154
+ demo.launch(
155
+ server_name="0.0.0.0",
156
+ server_port=7860,
157
+ share=False
158
+ )
config.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ • ๋ฐ ์ƒ์ˆ˜ ์ •์˜
3
+ """
4
+
5
+ # ์•ฑ ๊ธฐ๋ณธ ์„ค์ •
6
+ APP_CONFIG = {
7
+ "title": "๋ฐˆ ์ƒ์„ฑ & AI ์ฑ—๋ด‡ ์„œ๋น„์Šค",
8
+ "description": "๋ฐˆ์„ ๋งŒ๋“ค๊ณ  AI์™€ ๋Œ€ํ™”ํ•˜๋Š” ์˜ฌ์ธ์› ์„œ๋น„์Šค",
9
+ "version": "1.0.0"
10
+ }
11
+
12
+ # ๋ฐˆ ํ…œํ”Œ๋ฆฟ ์ •์˜
13
+ MEME_TEMPLATES = {
14
+ "๋“œ๋ ˆ์ดํฌ ๋ฐˆ": {
15
+ "description": "๋“œ๋ ˆ์ดํฌ๊ฐ€ ๊ฑฐ๋ถ€ํ•˜๊ณ  ์Šน์ธํ•˜๋Š” ๋ฐˆ",
16
+ "image_path": "drake_template.jpg",
17
+ "text_positions": [(50, 100), (50, 300)]
18
+ },
19
+ "๋””์ŠคํŠธ๋ž™ํ‹ฐ๋“œ ๋ณด์ดํ”„๋ Œ๋“œ": {
20
+ "description": "๋‹ค๋ฅธ ์—ฌ์ž๋ฅผ ๋ณด๋Š” ๋‚จ์ž์นœ๊ตฌ ๋ฐˆ",
21
+ "image_path": "distracted_boyfriend.jpg",
22
+ "text_positions": [(100, 50), (300, 50), (500, 50)]
23
+ },
24
+ "์ฒด์ธ์ง€ ๋งˆ์ด ๋งˆ์ธ๋“œ": {
25
+ "description": "๋‚ด ๋งˆ์Œ์„ ๋ฐ”๊ฟ”๋ด ๋ฐˆ",
26
+ "image_path": "change_my_mind.jpg",
27
+ "text_positions": [(200, 150)]
28
+ },
29
+ "์› ๋‘์Šค ๋‚ซ ์‹ฌํ”Œ๋ฆฌ": {
30
+ "description": "๋‹จ์ˆœํžˆ ~ํ•˜์ง€ ์•Š๋Š”๋‹ค ๋ฐˆ",
31
+ "image_path": "one_does_not_simply.jpg",
32
+ "text_positions": [(150, 50), (150, 350)]
33
+ }
34
+ }
35
+
36
+ # ์ฑ—๋ด‡ ๊ธฐ๋ณธ ์‘๋‹ต
37
+ CHATBOT_RESPONSES = {
38
+ "greetings": [
39
+ "์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฌด์—‡์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”?",
40
+ "๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค! ๊ถ๊ธˆํ•œ ๊ฒƒ์ด ์žˆ์œผ์‹œ๋ฉด ์–ธ์ œ๋“  ๋ฌผ์–ด๋ณด์„ธ์š”.",
41
+ "์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฐˆ ์ƒ์„ฑ์ด๋‚˜ ๋‹ค๋ฅธ ๋„์›€์ด ํ•„์š”ํ•˜์‹œ๋ฉด ๋ง์”€ํ•ด์ฃผ์„ธ์š”."
42
+ ],
43
+ "meme_help": [
44
+ "๋ฐˆ ์ƒ์„ฑ๊ธฐ ํƒญ์—์„œ ํ…œํ”Œ๋ฆฟ์„ ์„ ํƒํ•˜๊ณ  ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค!",
45
+ "์ธ๊ธฐ ์žˆ๋Š” ๋ฐˆ ํ…œํ”Œ๋ฆฟ๋“ค์„ ์ค€๋น„ํ•ด๋‘์—ˆ์–ด์š”. ์ฐฝ์˜์ ์ธ ํ…์ŠคํŠธ๋ฅผ ๋„ฃ์–ด๋ณด์„ธ์š”!",
46
+ "์ƒ๋‹จ๊ณผ ํ•˜๋‹จ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์žฌ๋ฏธ์žˆ๋Š” ๋ฐˆ์ด ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค."
47
+ ],
48
+ "default": [
49
+ "ํฅ๋ฏธ๋กœ์šด ์งˆ๋ฌธ์ด๋„ค์š”! ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ด์ฃผ์‹œ๊ฒ ์–ด์š”?",
50
+ "๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋” ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์œผ๋กœ ์–ด๋–ค ๋ถ€๋ถ„์ด ๊ถ๊ธˆํ•˜์‹ ๊ฐ€์š”?",
51
+ "์ข‹์€ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค! ์–ด๋–ค ๋„์›€์ด ํ•„์š”ํ•˜์‹ ์ง€ ๋ง์”€ํ•ด์ฃผ์„ธ์š”."
52
+ ]
53
+ }
54
+
55
+ # ์ƒ‰์ƒ ๋ฐ ์Šคํƒ€์ผ ์„ค์ •
56
+ STYLE_CONFIG = {
57
+ "primary_color": "#FF6B6B",
58
+ "secondary_color": "#4ECDC4",
59
+ "background_color": "#F7F9FC",
60
+ "text_color": "#2C3E50",
61
+ "font_family": "Arial, sans-serif"
62
+ }
63
+
64
+ # API ๋ฐ ์™ธ๋ถ€ ์„œ๋น„์Šค ์„ค์ •
65
+ API_CONFIG = {
66
+ "huggingface_api_url": "https://api-inference.huggingface.co/models/",
67
+ "timeout": 30,
68
+ "max_retries": 3
69
+ }
utils.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋“ค
3
+ """
4
+
5
+ import random
6
+ import requests
7
+ from PIL import Image, ImageDraw, ImageFont
8
+ import io
9
+ from config import CHATBOT_RESPONSES, MEME_TEMPLATES
10
+
11
+ def create_meme_image(template_info, top_text, bottom_text):
12
+ """๋ฐˆ ์ด๋ฏธ์ง€ ์ƒ์„ฑ"""
13
+ try:
14
+ # ๊ธฐ๋ณธ ์บ”๋ฒ„์Šค ์ƒ์„ฑ (์‹ค์ œ ํ™˜๊ฒฝ์—์„œ๋Š” ํ…œํ”Œ๋ฆฟ ์ด๋ฏธ์ง€ ๋กœ๋“œ)
15
+ width, height = 500, 400
16
+ img = Image.new('RGB', (width, height), color='white')
17
+ draw = ImageDraw.Draw(img)
18
+
19
+ # ํฐํŠธ ์„ค์ • (๊ธฐ๋ณธ ํฐํŠธ ์‚ฌ์šฉ)
20
+ try:
21
+ font_large = ImageFont.truetype("arial.ttf", 32)
22
+ font_small = ImageFont.truetype("arial.ttf", 24)
23
+ except:
24
+ font_large = ImageFont.load_default()
25
+ font_small = ImageFont.load_default()
26
+
27
+ # ๋ฐฐ๊ฒฝ ๊ทธ๋ผ๋ฐ์ด์…˜ ํšจ๊ณผ
28
+ for y in range(height):
29
+ color_value = int(255 * (y / height))
30
+ color = (color_value, color_value, 255)
31
+ draw.line([(0, y), (width, y)], fill=color)
32
+
33
+ # ํ…์ŠคํŠธ ์ถ”๊ฐ€
34
+ if top_text:
35
+ # ์ƒ๋‹จ ํ…์ŠคํŠธ
36
+ text_width = draw.textlength(top_text, font=font_large)
37
+ x = (width - text_width) // 2
38
+ y = 30
39
+ # ํ…์ŠคํŠธ ์™ธ๊ณฝ์„ 
40
+ for dx in [-1, 0, 1]:
41
+ for dy in [-1, 0, 1]:
42
+ if dx != 0 or dy != 0:
43
+ draw.text((x+dx, y+dy), top_text, font=font_large, fill='black')
44
+ draw.text((x, y), top_text, font=font_large, fill='white')
45
+
46
+ if bottom_text:
47
+ # ํ•˜๋‹จ ํ…์ŠคํŠธ
48
+ text_width = draw.textlength(bottom_text, font=font_large)
49
+ x = (width - text_width) // 2
50
+ y = height - 70
51
+ # ํ…์ŠคํŠธ ์™ธ๊ณฝ์„ 
52
+ for dx in [-1, 0, 1]:
53
+ for dy in [-1, 0, 1]:
54
+ if dx != 0 or dy != 0:
55
+ draw.text((x+dx, y+dy), bottom_text, font=font_large, fill='black')
56
+ draw.text((x, y), bottom_text, font=font_large, fill='white')
57
+
58
+ return img
59
+
60
+ except Exception as e:
61
+ print(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์˜ค๋ฅ˜: {e}")
62
+ return create_error_image(str(e))
63
+
64
+ def create_error_image(error_msg):
65
+ """์—๋Ÿฌ ์ด๋ฏธ์ง€ ์ƒ์„ฑ"""
66
+ img = Image.new('RGB', (400, 300), color='red')
67
+ draw = ImageDraw.Draw(img)
68
+ try:
69
+ font = ImageFont.load_default()
70
+ draw.text((50, 150), f"์˜ค๋ฅ˜: {error_msg}", font=font, fill='white')
71
+ except:
72
+ pass
73
+ return img
74
+
75
+ def add_text_to_image(img, text, position, font_size=32, color='white'):
76
+ """์ด๋ฏธ์ง€์— ํ…์ŠคํŠธ ์ถ”๊ฐ€"""
77
+ draw = ImageDraw.Draw(img)
78
+ try:
79
+ font = ImageFont.truetype("arial.ttf", font_size)
80
+ except:
81
+ font = ImageFont.load_default()
82
+
83
+ x, y = position
84
+ # ์™ธ๊ณฝ์„  ํšจ๊ณผ
85
+ for dx in [-1, 0, 1]:
86
+ for dy in [-1, 0, 1]:
87
+ if dx != 0 or dy != 0:
88
+ draw.text((x+dx, y+dy), text, font=font, fill='black')
89
+ draw.text((x, y), text, font=font, fill=color)
90
+
91
+ return img
92
+
93
+ def generate_response(message, qa_pipeline=None, text_generator=None):
94
+ """์ฑ—๋ด‡ ์‘๋‹ต ์ƒ์„ฑ"""
95
+ message_lower = message.lower()
96
+
97
+ # ์ธ์‚ฌ๋ง ์ฒ˜๋ฆฌ
98
+ if any(greeting in message_lower for greeting in ['์•ˆ๋…•', 'ํ•˜์ด', 'ํ—ฌ๋กœ', '๋ฐ˜๊ฐ€']):
99
+ return random.choice(CHATBOT_RESPONSES["greetings"])
100
+
101
+ # ๋ฐˆ ๊ด€๋ จ ์งˆ๋ฌธ ์ฒ˜๋ฆฌ
102
+ if any(word in message_lower for word in ['๋ฐˆ', 'meme', '์ด๋ฏธ์ง€', '์ƒ์„ฑ']):
103
+ return random.choice(CHATBOT_RESPONSES["meme_help"])
104
+
105
+ # AI ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•œ ์‘๋‹ต ์ƒ์„ฑ ์‹œ๋„
106
+ if qa_pipeline and len(message) > 10:
107
+ try:
108
+ # ๊ฐ„๋‹จํ•œ ์ปจํ…์ŠคํŠธ๋กœ QA ์ˆ˜ํ–‰
109
+ context = "์ด๊ฒƒ์€ ๋ฐˆ ์ƒ์„ฑ๊ณผ AI ์ฑ„ํŒ… ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋“ค์ด ์žฌ๋ฏธ์žˆ๋Š” ๋ฐˆ์„ ๋งŒ๋“ค๊ณ  AI์™€ ๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
110
+ result = qa_pipeline(question=message, context=context)
111
+ if result and 'answer' in result:
112
+ return f"๐Ÿค– {result['answer']}"
113
+ except Exception as e:
114
+ print(f"QA ํŒŒ์ดํ”„๋ผ์ธ ์˜ค๋ฅ˜: {e}")
115
+
116
+ # ํ…์ŠคํŠธ ์ƒ์„ฑ ๋ชจ๋ธ ์‚ฌ์šฉ ์‹œ๋„
117
+ if text_generator:
118
+ try:
119
+ result = text_generator(f"์‚ฌ์šฉ์ž: {message}\n๋ด‡:", max_length=50, num_return_sequences=1)
120
+ if result and len(result) > 0:
121
+ generated_text = result[0]['generated_text']
122
+ bot_response = generated_text.split('๋ด‡:')[-1].strip()
123
+ if bot_response and len(bot_response) > 5:
124
+ return f"๐Ÿค– {bot_response}"
125
+ except Exception as e:
126
+ print(f"ํ…์ŠคํŠธ ์ƒ์„ฑ ์˜ค๋ฅ˜: {e}")
127
+
128
+ # ๊ธฐ๋ณธ ์‘๋‹ต
129
+ return random.choice(CHATBOT_RESPONSES["default"])
130
+
131
+ def validate_input(text, max_length=100):
132
+ """์ž…๋ ฅ ํ…์ŠคํŠธ ๊ฒ€์ฆ"""
133
+ if not text or not text.strip():
134
+ return False, "ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
135
+
136
+ if len(text) > max_length:
137
+ return False, f"ํ…์ŠคํŠธ๊ฐ€ ๋„ˆ๋ฌด ๊น๋‹ˆ๋‹ค. {max_length}์ž ์ดํ•˜๋กœ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
138
+
139
+ # ํŠน์ˆ˜๋ฌธ์ž ํ•„ํ„ฐ๋ง (์„ ํƒ์ )
140
+ forbidden_chars = ['<', '>', '&', '"', "'"]
141
+ if any(char in text for char in forbidden_chars):
142
+ return False, "ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” ํŠน์ˆ˜๋ฌธ์ž๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค."
143
+
144
+ return True, "๊ฒ€์ฆ ํ†ต๊ณผ"
145
+
146
+ def get_random_meme_idea():
147
+ """๋žœ๋ค ๋ฐˆ ์•„์ด๋””์–ด ์ œ๊ณต"""
148
+ ideas = [
149
+ ("When you finish your project", "But remember you didn't write tests"),
150
+ ("Me: I'll go to bed early tonight", "Also me at 3 AM:"),
151
+ ("CSS in theory", "CSS in practice"),
152
+ ("My code works", "I have no idea why"),
153
+ ("Expected behavior", "Actual behavior")
154
+ ]
155
+ return random.choice(ideas)
156
+
157
+ def format_trend_data(keyword, data=None):
158
+ """ํŠธ๋ Œ๋“œ ๋ฐ์ดํ„ฐ ํฌ๋งทํŒ…"""
159
+ if not data:
160
+ # ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
161
+ popularity = random.randint(60, 95)
162
+ growth = random.randint(-20, 50)
163
+ related_terms = random.sample([
164
+ '๋ฐ”์ด๋Ÿด', '์ธ๊ธฐ', 'ํŠธ๋ Œ๋“œ', '์›ƒ๊ธด', '์žฌ๋ฏธ์žˆ๋Š”',
165
+ 'ํ•ซ์ด์Šˆ', 'ํ™”์ œ', '๋ฐˆ', 'ํŒจ๋Ÿฌ๋””', '์œ ๋จธ'
166
+ ], 3)
167
+
168
+ result = f"""
169
+ ๐Ÿ“Š '{keyword}' ํŠธ๋ Œ๋“œ ๋ถ„์„ ๊ฒฐ๊ณผ
170
+
171
+ ๐Ÿ”ฅ ์ธ๊ธฐ๋„: {popularity}/100
172
+ ๐Ÿ“ˆ ์„ฑ์žฅ๋ฅ : {growth:+d}%
173
+ ๐Ÿท๏ธ ๊ด€๋ จ ํ‚ค์›Œ๋“œ: {', '.join(related_terms)}
174
+ โฐ ๋ถ„์„ ์‹œ๊ฐ„: ๋ฐฉ๊ธˆ ์ „
175
+
176
+ ๐Ÿ’ก ์ถ”์ฒœ: {'์ƒ์Šน ํŠธ๋ Œ๋“œ์ž…๋‹ˆ๋‹ค!' if growth > 0 else '๊ด€์‹ฌ์„ ๋Œ ์ˆ˜ ์žˆ๋Š” ํ‚ค์›Œ๋“œ์ž…๋‹ˆ๋‹ค.'}
177
+ """
178
+ return result.strip()
179
+
180
+ return str(data)