Nevaehni commited on
Commit
b0d8cff
Β·
1 Parent(s): c9ca818
Files changed (3) hide show
  1. Dockerfile +16 -0
  2. app.py +262 -0
  3. requirements.txt +4 -0
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.10
5
+
6
+ RUN useradd -m -u 1000 user
7
+ USER user
8
+ ENV PATH="/home/user/.local/bin:$PATH"
9
+
10
+ WORKDIR /app
11
+
12
+ COPY --chown=user ./requirements.txt requirements.txt
13
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
+
15
+ COPY --chown=user . /app
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # discord_bot.py
2
+
3
+ import asyncio
4
+ import logging
5
+ import os
6
+ import re
7
+ import sys
8
+
9
+ import discord
10
+ import requests # Ensure 'requests' is installed
11
+ from discord import Embed
12
+ from discord.ext import commands
13
+ from gradio_client import Client
14
+ from gradio_client.exceptions import AppError # Updated import
15
+
16
+ # **Fetch Discord Bot Token from Environment Variable**
17
+ DISCORD_BOT_TOKEN = os.environ.get('DISCORD_BOT_TOKEN')
18
+ HF_TOKEN = os.environ.get('HF_TOKEN') # Fetch the HF_TOKEN
19
+
20
+ if not DISCORD_BOT_TOKEN:
21
+ print("Error: The environment variable 'DISCORD_BOT_TOKEN' is not set.")
22
+ sys.exit(1)
23
+
24
+ if not HF_TOKEN:
25
+ print("Error: The environment variable 'HF_TOKEN' is not set.")
26
+ sys.exit(1)
27
+
28
+ # Configure logging
29
+ logging.basicConfig(
30
+ level=logging.INFO, # Change to DEBUG for more detailed logs
31
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
32
+ handlers=[
33
+ logging.StreamHandler(sys.stdout)
34
+ ]
35
+ )
36
+ logger = logging.getLogger(__name__)
37
+
38
+ # Intents are required for accessing certain Discord gateway events
39
+ intents = discord.Intents.default()
40
+ intents.message_content = True # Enable access to message content
41
+
42
+ # Initialize the bot with command prefix '!' and the specified intents
43
+ bot = commands.Bot(command_prefix='!', intents=intents, help_command=None)
44
+
45
+ # Regular expression to parse the prompt parameter
46
+ PROMPT_REGEX = re.compile(r'prompt\s*=\s*"(.*?)"')
47
+
48
+ # Initialize the Gradio client with hf_token
49
+ GRADIO_CLIENT = Client("Nevaehni/FLUX.1-schnell", hf_token=HF_TOKEN)
50
+
51
+
52
+ @bot.event
53
+ async def on_ready():
54
+ """Event handler triggered when the bot is ready."""
55
+ logger.info(f'Logged in as {bot.user} (ID: {bot.user.id})')
56
+ logger.info('------')
57
+
58
+
59
+ def parse_prompt(command: str) -> str:
60
+ """
61
+ Parse the prompt from the command string.
62
+
63
+ Args:
64
+ command (str): The command message content.
65
+
66
+ Returns:
67
+ str: The extracted prompt or an empty string if not found.
68
+ """
69
+ match = PROMPT_REGEX.search(command)
70
+ if match:
71
+ return match.group(1).strip()
72
+ return ''
73
+
74
+
75
+ def create_example_embed() -> Embed:
76
+ """
77
+ Create an embed message with an example !generate command.
78
+
79
+ Returns:
80
+ Embed: The Discord embed object.
81
+ """
82
+ example_command = '!generate prompt="High resolution serene landscape with text \'cucolina\'. seed:1"'
83
+ embed = Embed(
84
+ description=f"```\n{example_command}\n```",
85
+ color=discord.Color.blue()
86
+ )
87
+ return embed
88
+
89
+
90
+ @bot.command(name='generate')
91
+ async def generate(ctx: commands.Context, *, args: str = None):
92
+ """
93
+ Command handler for !generate. Generates content based on the provided prompt.
94
+
95
+ Args:
96
+ ctx (commands.Context): The context in which the command was invoked.
97
+ args (str, optional): The arguments passed with the command.
98
+ """
99
+ if not args:
100
+ # No parameters provided, send example command without copy button
101
+ embed = create_example_embed()
102
+ await ctx.send(embed=embed)
103
+ return
104
+
105
+ # Parse the prompt from the arguments
106
+ prompt = parse_prompt(args)
107
+
108
+ if not prompt:
109
+ # Prompt parameter not found or empty
110
+ await ctx.send("❌ **Error:** Prompt cannot be empty. Please provide a valid input.")
111
+ return
112
+
113
+ # Acknowledge the command and indicate processing
114
+ processing_message = await ctx.send("πŸ”„ Generating your content, please wait...")
115
+
116
+ try:
117
+ logger.info(f"Received prompt: {prompt}")
118
+
119
+ # Define an asynchronous wrapper for the predict call
120
+ async def call_predict():
121
+ return GRADIO_CLIENT.predict(param_0=prompt, api_name="/predict")
122
+
123
+ # Set a timeout for the predict call (e.g., 60 seconds)
124
+ response = await asyncio.wait_for(call_predict(), timeout=60)
125
+
126
+ logger.info(f"API response: {response}")
127
+
128
+ # **Debugging: Log the actual response**
129
+ logger.debug(f"API Response: {response}")
130
+
131
+ # Reconstruct the exact command used by the user
132
+ command_used = ctx.message.content.strip()
133
+
134
+ # Handle different response structures
135
+ if isinstance(response, dict):
136
+ # Check if 'url' key exists
137
+ url = response.get('url')
138
+ if url:
139
+ if isinstance(url, str):
140
+ # Embed the image if it's an image URL
141
+ if url.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp')):
142
+ embed = Embed(title="🎨 Generated Image", color=discord.Color.green())
143
+ embed.set_image(url=url)
144
+ # Add prompt as a field with code block
145
+ embed.add_field(name="Prompt", value=f"```\n{command_used}\n```", inline=False)
146
+ # Send the embed and mention the user
147
+ await ctx.send(content=f"{ctx.author.mention}", embed=embed)
148
+ else:
149
+ # If not an image, send the URL directly
150
+ embed = Embed(title="πŸ”— Generated Content", description=url, color=discord.Color.green())
151
+ embed.add_field(name="Prompt", value=f"```\n{command_used}\n```", inline=False)
152
+ await ctx.send(content=f"{ctx.author.mention}", embed=embed)
153
+ else:
154
+ # 'url' exists but is not a string
155
+ await ctx.send("❌ **Error:** Received an invalid URL format from the API.")
156
+ else:
157
+ # 'url' key does not exist
158
+ await ctx.send("❌ **Error:** The API response does not contain a 'url' key.")
159
+ elif isinstance(response, str):
160
+ # Assume the response is a file path
161
+ file_path = response
162
+ if os.path.isfile(file_path):
163
+ try:
164
+ # Extract the file name
165
+ file_name = os.path.basename(file_path)
166
+
167
+ # Create a Discord File object
168
+ discord_file = discord.File(file_path, filename=file_name)
169
+
170
+ # Create an embed with the image
171
+ embed = Embed(title="🎨 Generated Image", color=discord.Color.green())
172
+ embed.set_image(url=f"attachment://{file_name}")
173
+
174
+ # Add prompt as a field with code block
175
+ embed.add_field(name="Prompt", value=f"```\n{command_used}\n```", inline=False)
176
+
177
+ # Send the embed with the file and mention the user
178
+ await ctx.send(content=f"{ctx.author.mention}", embed=embed, file=discord_file)
179
+
180
+ logger.info(f"Sent image from {file_path} to Discord.")
181
+ except Exception as e:
182
+ logger.error(f"Failed to send image to Discord: {e}")
183
+ await ctx.send("❌ **Error:** Failed to send the generated image to Discord.")
184
+ else:
185
+ await ctx.send("❌ **Error:** The API returned an invalid file path.")
186
+ elif isinstance(response, list):
187
+ # Handle list responses if applicable
188
+ if len(response) > 0 and isinstance(response[0], dict):
189
+ first_item = response[0]
190
+ url = first_item.get('url')
191
+ if url and isinstance(url, str):
192
+ if url.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp')):
193
+ embed = Embed(title="🎨 Generated Image", color=discord.Color.green())
194
+ embed.set_image(url=url)
195
+ embed.add_field(name="Prompt", value=f"```\n{command_used}\n```", inline=False)
196
+ await ctx.send(content=f"{ctx.author.mention}", embed=embed)
197
+ else:
198
+ embed = Embed(title="πŸ”— Generated Content", description=url, color=discord.Color.green())
199
+ embed.add_field(name="Prompt", value=f"```\n{command_used}\n```", inline=False)
200
+ await ctx.send(content=f"{ctx.author.mention}", embed=embed)
201
+ else:
202
+ await ctx.send("❌ **Error:** Received an invalid URL format from the API.")
203
+ else:
204
+ await ctx.send("❌ **Error:** The API response is an unexpected list structure.")
205
+ else:
206
+ # Response is neither dict, str, nor list
207
+ await ctx.send("❌ **Error:** Unexpected response format from the API.")
208
+ except asyncio.TimeoutError:
209
+ logger.error("API request timed out.")
210
+ await ctx.send("⏰ **Error:** The request to the API timed out. Please try again later.")
211
+ except AppError as e:
212
+ logger.error(f"API Error: {str(e)}")
213
+ await ctx.send(f"❌ **API Error:** {str(e)}")
214
+ except requests.exceptions.ConnectionError:
215
+ logger.error("Failed to connect to the API.")
216
+ await ctx.send("⚠️ **Error:** Failed to connect to the API. Please check your network connection.")
217
+ except Exception as e:
218
+ logger.exception("An unexpected error occurred.")
219
+ await ctx.send(f"❌ **Error:** An unexpected error occurred: {str(e)}")
220
+ finally:
221
+ # Delete the processing message
222
+ await processing_message.delete()
223
+
224
+
225
+ @bot.event
226
+ async def on_command_error(ctx: commands.Context, error):
227
+ """
228
+ Global error handler for command errors.
229
+
230
+ Args:
231
+ ctx (commands.Context): The context in which the error occurred.
232
+ error (Exception): The exception that was raised.
233
+ """
234
+ if isinstance(error, commands.CommandNotFound):
235
+ await ctx.send("❓ **Error:** Unknown command. Please use `!generate` to generate content.")
236
+ elif isinstance(error, commands.CommandOnCooldown):
237
+ await ctx.send(f"⏳ **Please wait {error.retry_after:.2f} seconds before using this command again.**")
238
+ else:
239
+ await ctx.send(f"❌ **Error:** {str(error)}")
240
+ logger.error(f"Unhandled command error: {str(error)}")
241
+
242
+
243
+ async def handle_root(request):
244
+ return web.Response(text="DarkMuse GOES VROOOOM", status=200)
245
+
246
+
247
+ async def start_web_server():
248
+ app = web.Application()
249
+ app.router.add_get('/', handle_root)
250
+ runner = web.AppRunner(app)
251
+ await runner.setup()
252
+ site = web.TCPSite(runner, '0.0.0.0', 7860)
253
+ await site.start()
254
+
255
+
256
+ # Run the bot
257
+ if __name__ == '__main__':
258
+ try:
259
+ bot.run(DISCORD_BOT_TOKEN)
260
+ bot.loop.create_task(start_web_server())
261
+ except Exception as e:
262
+ logger.exception(f"Failed to run the bot: {e}")
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ discord.py==2.4.0
2
+ requests==2.32.3
3
+ gradio-client==1.5.1
4
+ aiohttp==3.11.9