Asad235 commited on
Commit
a59f2bc
Β·
verified Β·
1 Parent(s): 9ac75ff

Update app3.py

Browse files
Files changed (1) hide show
  1. app3.py +159 -137
app3.py CHANGED
@@ -1,7 +1,7 @@
1
  #!/usr/bin/env python3
2
  """
3
- Discord Bot for Voiceover Generation
4
- Reads prompts.csv from server and generates voiceovers
5
  """
6
 
7
  import discord
@@ -9,16 +9,19 @@ from discord.ext import commands
9
  import asyncio
10
  import edge_tts
11
  import csv
12
- import os
13
  import tempfile
14
- from pathlib import Path
15
 
16
  # ============================================================
17
  # CONFIGURATION
18
  # ============================================================
19
  TOKEN = "MTQ2ODk2MDIzMjg4MDQ3MjIwNg.GkZcck.IotGRnly_kDwVwUkK9MZ_KXaD6TYI-4lGFKepI"
20
 
21
- # Voice styles for Edge TTS
 
 
 
22
  VOICE_STYLES = {
23
  "1": {"name": "Professional Female", "voice": "en-US-JennyNeural"},
24
  "2": {"name": "Professional Male", "voice": "en-US-GuyNeural"},
@@ -27,25 +30,66 @@ VOICE_STYLES = {
27
  "5": {"name": "Friendly Female", "voice": "en-US-AnaNeural"},
28
  }
29
 
30
- # CSV path (update with your actual path)
31
- CSV_PATH = "/home/container/prompts.csv" # Change this to your server path
32
 
33
  # ============================================================
34
  # BOT SETUP
35
  # ============================================================
36
  intents = discord.Intents.default()
37
  intents.message_content = True
 
38
  bot = commands.Bot(command_prefix="!", intents=intents)
39
 
40
  # ============================================================
41
- # VOICEOVER GENERATION FUNCTION
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # ============================================================
43
  def generate_natural_voiceover(raw_prompt):
44
  """Convert technical prompt to natural voiceover"""
45
- import random
46
 
47
- intros = ["Watch how", "See how", "Discover how", "Learn how"]
48
- outros = ["That's engineering in action!", "Simple mechanics, powerful results!"]
49
 
50
  if " in " in raw_prompt:
51
  parts = raw_prompt.split(" in ", 1)
@@ -71,138 +115,102 @@ async def text_to_speech(text, voice_id, output_path):
71
  await communicate.save(output_path)
72
  return output_path
73
 
74
- # ============================================================
75
- # READ CSV FUNCTION
76
- # ============================================================
77
- def read_prompts_from_csv(csv_path):
78
- """Read prompts from CSV file"""
79
- prompts = []
80
-
81
- if not os.path.exists(csv_path):
82
- return None, f"❌ CSV file not found at: {csv_path}"
83
-
84
- try:
85
- with open(csv_path, 'r', encoding='utf-8') as f:
86
- reader = csv.DictReader(f)
87
- for row in reader:
88
- prompt = row.get('prompt', row.get('prompt_text', row.get('text', '')))
89
- if prompt:
90
- prompts.append(prompt)
91
-
92
- return prompts, f"βœ… Found {len(prompts)} prompts in CSV"
93
- except Exception as e:
94
- return None, f"❌ Error reading CSV: {str(e)}"
95
-
96
  # ============================================================
97
  # DISCORD COMMANDS
98
  # ============================================================
99
 
100
  @bot.event
101
  async def on_ready():
 
102
  print(f"βœ… Bot is ready!")
103
  print(f"πŸ€– Name: {bot.user.name}")
104
- print(f"πŸ“ CSV Path: {CSV_PATH}")
 
 
 
 
105
  print("=" * 50)
106
 
107
- @bot.command(name="ping")
108
- async def ping(ctx):
109
- """Check if bot is alive"""
110
- await ctx.send(f"πŸ“ Pong! Latency: {round(bot.latency * 1000)}ms")
 
111
 
112
  @bot.command(name="list")
113
- async def list_prompts(ctx):
114
- """List all prompts from CSV"""
115
- prompts, msg = read_prompts_from_csv(CSV_PATH)
 
 
 
 
 
 
 
116
 
117
- if prompts is None:
118
- await ctx.send(msg)
119
  return
120
 
121
- # Send first 10 prompts
122
- response = f"**πŸ“‹ Total Prompts:** {len(prompts)}\n\n"
123
- for i, p in enumerate(prompts[:10], 1):
124
- response += f"`{i}. {p[:80]}...`\n"
125
 
126
- if len(prompts) > 10:
127
- response += f"\n*... and {len(prompts) - 10} more*"
 
128
 
129
  await ctx.send(response)
130
 
 
 
 
 
 
 
 
 
131
  @bot.command(name="generate")
132
- async def generate_prompt(ctx, prompt_id: int = None):
133
- """Generate voiceover for a specific prompt ID or all"""
134
-
135
- prompts, msg = read_prompts_from_csv(CSV_PATH)
 
 
 
136
 
137
- if prompts is None:
138
- await ctx.send(msg)
139
  return
140
 
141
- if prompt_id is not None:
142
- # Generate single prompt
143
- if prompt_id < 1 or prompt_id > len(prompts):
144
- await ctx.send(f"❌ Prompt ID must be between 1 and {len(prompts)}")
145
- return
146
-
147
- prompt_text = prompts[prompt_id - 1]
148
-
149
- await ctx.send(f"🎀 Generating voiceover for **Prompt #{prompt_id}**...\n`{prompt_text[:100]}...`")
150
-
151
- # Generate natural voiceover
152
- voiceover_text = generate_natural_voiceover(prompt_text)
153
-
154
- # Generate audio
155
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
156
- await text_to_speech(voiceover_text, "en-US-JennyNeural", tmp.name)
157
-
158
- # Send audio
159
- await ctx.send(f"βœ… **Prompt #{prompt_id}**\nπŸ“ Script: {voiceover_text[:200]}...")
160
- await ctx.send(file=discord.File(tmp.name, filename=f"prompt_{prompt_id}.mp3"))
161
 
162
- else:
163
- # Generate all prompts
164
- await ctx.send(f"🎀 Generating voiceovers for **{len(prompts)}** prompts... This may take a while!")
165
-
166
- success_count = 0
167
- for idx, prompt_text in enumerate(prompts, 1):
168
- voiceover_text = generate_natural_voiceover(prompt_text)
169
-
170
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
171
- await text_to_speech(voiceover_text, "en-US-JennyNeural", tmp.name)
172
- await ctx.send(file=discord.File(tmp.name, filename=f"prompt_{idx}.mp3"))
173
-
174
- success_count += 1
175
- await asyncio.sleep(1) # Rate limit
176
-
177
- await ctx.send(f"βœ… Completed! Generated {success_count} voiceovers!")
178
 
179
  @bot.command(name="voice")
180
  async def generate_with_voice(ctx, prompt_id: int, voice_style: str = "1"):
181
- """Generate voiceover with specific voice style
182
- Usage: !voice 1 2 (prompt_id=1, voice_style=2)
183
-
184
- Voice styles:
185
- 1 = Professional Female
186
- 2 = Professional Male
187
- 3 = British Female
188
- 4 = Deep Male
189
- 5 = Friendly Female
190
  """
191
-
192
- prompts, msg = read_prompts_from_csv(CSV_PATH)
193
-
194
- if prompts is None:
195
- await ctx.send(msg)
196
  return
197
 
198
- if prompt_id < 1 or prompt_id > len(prompts):
199
- await ctx.send(f"❌ Prompt ID must be between 1 and {len(prompts)}")
200
  return
201
 
202
  voice = VOICE_STYLES.get(voice_style, VOICE_STYLES["1"])
203
- prompt_text = prompts[prompt_id - 1]
204
 
205
- await ctx.send(f"🎀 Generating with **{voice['name']}** voice for Prompt #{prompt_id}...")
206
 
207
  voiceover_text = generate_natural_voiceover(prompt_text)
208
 
@@ -211,31 +219,28 @@ async def generate_with_voice(ctx, prompt_id: int, voice_style: str = "1"):
211
  await ctx.send(file=discord.File(tmp.name, filename=f"prompt_{prompt_id}.mp3"))
212
 
213
  @bot.command(name="batch")
214
- async def batch_generate(ctx, start_id: int = 1, end_id: int = None, voice_style: str = "1"):
215
  """Generate voiceovers for a range of prompts
216
- Usage: !batch 1 10 2
217
  """
218
-
219
- prompts, msg = read_prompts_from_csv(CSV_PATH)
220
-
221
- if prompts is None:
222
- await ctx.send(msg)
223
  return
224
 
225
  if end_id is None:
226
- end_id = len(prompts)
227
 
228
- if start_id < 1 or end_id > len(prompts) or start_id > end_id:
229
- await ctx.send(f"❌ Invalid range. Use 1 to {len(prompts)}")
230
  return
231
 
232
  voice = VOICE_STYLES.get(voice_style, VOICE_STYLES["1"])
233
  total = end_id - start_id + 1
234
 
235
- await ctx.send(f"🎀 Generating {total} voiceovers (IDs {start_id}-{end_id}) with **{voice['name']}** voice...")
236
 
237
  for idx in range(start_id, end_id + 1):
238
- prompt_text = prompts[idx - 1]
239
  voiceover_text = generate_natural_voiceover(prompt_text)
240
 
241
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
@@ -246,43 +251,60 @@ async def batch_generate(ctx, start_id: int = 1, end_id: int = None, voice_style
246
 
247
  await ctx.send(f"βœ… Completed! Generated {total} voiceovers!")
248
 
249
- @bot.command(name="count")
250
- async def count_prompts(ctx):
251
- """Get total number of prompts in CSV"""
252
- prompts, msg = read_prompts_from_csv(CSV_PATH)
 
 
253
 
254
- if prompts is None:
255
- await ctx.send(msg)
256
- else:
257
- await ctx.send(f"πŸ“Š Total prompts in CSV: **{len(prompts)}**")
 
 
 
 
 
 
258
 
259
  @bot.command(name="info")
260
  async def bot_info(ctx):
261
  """Show bot information"""
262
  embed = discord.Embed(
263
  title="πŸ€– Voiceover Generator Bot",
264
- description="Generates voiceovers from CSV prompts",
265
  color=discord.Color.blue()
266
  )
267
- embed.add_field(name="CSV Path", value=CSV_PATH, inline=False)
268
- embed.add_field(name="Voice Styles", value=", ".join([f"{k}={v['name']}" for k, v in VOICE_STYLES.items()]), inline=False)
269
- embed.add_field(name="Commands", value="""`!list` - List prompts
 
 
270
  `!count` - Count prompts
271
  `!generate <id>` - Generate voiceover
272
  `!voice <id> <style>` - Generate with style
273
  `!batch <start> <end>` - Batch generate
274
- `!ping` - Check bot""", inline=False)
 
275
 
276
  await ctx.send(embed=embed)
277
 
 
 
 
 
 
278
  # ============================================================
279
  # RUN BOT
280
  # ============================================================
281
  if __name__ == "__main__":
282
- print("=" * 60)
283
  print("πŸ€– Discord Voiceover Bot")
284
- print("=" * 60)
285
- print(f"πŸ“ CSV Path: {CSV_PATH}")
286
  print("πŸš€ Starting bot...")
 
287
 
288
  bot.run(TOKEN)
 
1
  #!/usr/bin/env python3
2
  """
3
+ Discord Bot - Reads CSV from #general channel
4
+ CSV file is uploaded in #general, bot reads it from there
5
  """
6
 
7
  import discord
 
9
  import asyncio
10
  import edge_tts
11
  import csv
12
+ import io
13
  import tempfile
14
+ import random
15
 
16
  # ============================================================
17
  # CONFIGURATION
18
  # ============================================================
19
  TOKEN = "MTQ2ODk2MDIzMjg4MDQ3MjIwNg.GkZcck.IotGRnly_kDwVwUkK9MZ_KXaD6TYI-4lGFKepI"
20
 
21
+ # Channel name where CSV is uploaded
22
+ CSV_CHANNEL_NAME = "general" # #general channel
23
+
24
+ # Voice styles
25
  VOICE_STYLES = {
26
  "1": {"name": "Professional Female", "voice": "en-US-JennyNeural"},
27
  "2": {"name": "Professional Male", "voice": "en-US-GuyNeural"},
 
30
  "5": {"name": "Friendly Female", "voice": "en-US-AnaNeural"},
31
  }
32
 
33
+ # Store prompts
34
+ csv_prompts = []
35
 
36
  # ============================================================
37
  # BOT SETUP
38
  # ============================================================
39
  intents = discord.Intents.default()
40
  intents.message_content = True
41
+ intents.attachments = True
42
  bot = commands.Bot(command_prefix="!", intents=intents)
43
 
44
  # ============================================================
45
+ # LOAD CSV FROM GENERAL CHANNEL
46
+ # ============================================================
47
+ async def load_csv_from_general():
48
+ """Read CSV file from #general channel"""
49
+ global csv_prompts
50
+
51
+ # Find #general channel
52
+ channel = None
53
+ for c in bot.get_all_channels():
54
+ if c.name == CSV_CHANNEL_NAME and isinstance(c, discord.TextChannel):
55
+ channel = c
56
+ break
57
+
58
+ if channel is None:
59
+ return False, f"❌ #{CSV_CHANNEL_NAME} channel not found!"
60
+
61
+ print(f"πŸ“ Searching in #{channel.name}...")
62
+
63
+ # Find message with CSV attachment
64
+ async for message in channel.history(limit=200):
65
+ if message.attachments:
66
+ for attachment in message.attachments:
67
+ if attachment.filename.endswith('.csv'):
68
+ csv_content = await attachment.read()
69
+ print(f"πŸ“„ Found CSV: {attachment.filename}")
70
+
71
+ # Parse CSV
72
+ csv_text = csv_content.decode('utf-8')
73
+ csv_reader = csv.DictReader(io.StringIO(csv_text))
74
+
75
+ csv_prompts = []
76
+ for row in csv_reader:
77
+ prompt = row.get('prompt', row.get('prompt_text', row.get('text', '')))
78
+ if prompt:
79
+ csv_prompts.append(prompt)
80
+
81
+ return True, f"βœ… Loaded {len(csv_prompts)} prompts from #{channel.name}"
82
+
83
+ return False, "❌ No CSV file found in #general channel!"
84
+
85
+ # ============================================================
86
+ # VOICEOVER GENERATION
87
  # ============================================================
88
  def generate_natural_voiceover(raw_prompt):
89
  """Convert technical prompt to natural voiceover"""
 
90
 
91
+ intros = ["Watch how", "See how", "Discover how", "Learn how", "Ever wondered how"]
92
+ outros = ["That's engineering in action!", "Simple mechanics, powerful results!", "That's how industry gets things done!"]
93
 
94
  if " in " in raw_prompt:
95
  parts = raw_prompt.split(" in ", 1)
 
115
  await communicate.save(output_path)
116
  return output_path
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  # ============================================================
119
  # DISCORD COMMANDS
120
  # ============================================================
121
 
122
  @bot.event
123
  async def on_ready():
124
+ print("=" * 50)
125
  print(f"βœ… Bot is ready!")
126
  print(f"πŸ€– Name: {bot.user.name}")
127
+ print(f"πŸ“ Looking in: #{CSV_CHANNEL_NAME} channel")
128
+
129
+ # Load CSV on startup
130
+ status, msg = await load_csv_from_general()
131
+ print(msg)
132
  print("=" * 50)
133
 
134
+ @bot.command(name="reload")
135
+ async def reload_csv(ctx):
136
+ """Reload CSV from #general channel"""
137
+ status, msg = await load_csv_from_general()
138
+ await ctx.send(msg)
139
 
140
  @bot.command(name="list")
141
+ async def list_prompts(ctx, page: int = 1):
142
+ """List prompts from CSV
143
+ Example: !list 2
144
+ """
145
+ if not csv_prompts:
146
+ await ctx.send("❌ No prompts loaded! Use `!reload` first.")
147
+ return
148
+
149
+ per_page = 10
150
+ total_pages = (len(csv_prompts) + per_page - 1) // per_page
151
 
152
+ if page < 1 or page > total_pages:
153
+ await ctx.send(f"❌ Page {page} not found. Total pages: {total_pages}")
154
  return
155
 
156
+ start = (page - 1) * per_page
157
+ end = min(start + per_page, len(csv_prompts))
 
 
158
 
159
+ response = f"**πŸ“‹ Prompts (Page {page}/{total_pages})** - Total: {len(csv_prompts)}\n\n"
160
+ for i in range(start, end):
161
+ response += f"`{i+1}. {csv_prompts[i][:70]}...`\n"
162
 
163
  await ctx.send(response)
164
 
165
+ @bot.command(name="count")
166
+ async def count_prompts(ctx):
167
+ """Get total number of prompts"""
168
+ if not csv_prompts:
169
+ await ctx.send("❌ No prompts loaded! Use `!reload` first.")
170
+ else:
171
+ await ctx.send(f"πŸ“Š Total prompts in CSV: **{len(csv_prompts)}**")
172
+
173
  @bot.command(name="generate")
174
+ async def generate_prompt(ctx, prompt_id: int):
175
+ """Generate voiceover for a specific prompt
176
+ Example: !generate 5
177
+ """
178
+ if not csv_prompts:
179
+ await ctx.send("❌ No prompts loaded! Use `!reload` first.")
180
+ return
181
 
182
+ if prompt_id < 1 or prompt_id > len(csv_prompts):
183
+ await ctx.send(f"❌ Prompt ID must be between 1 and {len(csv_prompts)}")
184
  return
185
 
186
+ prompt_text = csv_prompts[prompt_id - 1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
+ await ctx.send(f"🎀 **Generating voiceover for Prompt #{prompt_id}**...\nπŸ“ `{prompt_text[:100]}...`")
189
+
190
+ voiceover_text = generate_natural_voiceover(prompt_text)
191
+
192
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
193
+ await text_to_speech(voiceover_text, "en-US-JennyNeural", tmp.name)
194
+ await ctx.send(file=discord.File(tmp.name, filename=f"prompt_{prompt_id}.mp3"))
 
 
 
 
 
 
 
 
 
195
 
196
  @bot.command(name="voice")
197
  async def generate_with_voice(ctx, prompt_id: int, voice_style: str = "1"):
198
+ """Generate with specific voice style
199
+ Example: !voice 5 2
200
+ Styles: 1=Female, 2=Male, 3=British, 4=Deep, 5=Friendly
 
 
 
 
 
 
201
  """
202
+ if not csv_prompts:
203
+ await ctx.send("❌ No prompts loaded! Use `!reload` first.")
 
 
 
204
  return
205
 
206
+ if prompt_id < 1 or prompt_id > len(csv_prompts):
207
+ await ctx.send(f"❌ Prompt ID must be between 1 and {len(csv_prompts)}")
208
  return
209
 
210
  voice = VOICE_STYLES.get(voice_style, VOICE_STYLES["1"])
211
+ prompt_text = csv_prompts[prompt_id - 1]
212
 
213
+ await ctx.send(f"🎀 Generating with **{voice['name']}** for Prompt #{prompt_id}...")
214
 
215
  voiceover_text = generate_natural_voiceover(prompt_text)
216
 
 
219
  await ctx.send(file=discord.File(tmp.name, filename=f"prompt_{prompt_id}.mp3"))
220
 
221
  @bot.command(name="batch")
222
+ async def batch_generate(ctx, start_id: int, end_id: int = None, voice_style: str = "1"):
223
  """Generate voiceovers for a range of prompts
224
+ Example: !batch 1 10
225
  """
226
+ if not csv_prompts:
227
+ await ctx.send("❌ No prompts loaded! Use `!reload` first.")
 
 
 
228
  return
229
 
230
  if end_id is None:
231
+ end_id = len(csv_prompts)
232
 
233
+ if start_id < 1 or end_id > len(csv_prompts) or start_id > end_id:
234
+ await ctx.send(f"❌ Invalid range. Use 1 to {len(csv_prompts)}")
235
  return
236
 
237
  voice = VOICE_STYLES.get(voice_style, VOICE_STYLES["1"])
238
  total = end_id - start_id + 1
239
 
240
+ await ctx.send(f"🎀 Generating {total} voiceovers (IDs {start_id}-{end_id}) with **{voice['name']}**...")
241
 
242
  for idx in range(start_id, end_id + 1):
243
+ prompt_text = csv_prompts[idx - 1]
244
  voiceover_text = generate_natural_voiceover(prompt_text)
245
 
246
  with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
 
251
 
252
  await ctx.send(f"βœ… Completed! Generated {total} voiceovers!")
253
 
254
+ @bot.command(name="random")
255
+ async def random_prompt(ctx):
256
+ """Generate voiceover for a random prompt"""
257
+ if not csv_prompts:
258
+ await ctx.send("❌ No prompts loaded! Use `!reload` first.")
259
+ return
260
 
261
+ prompt_id = random.randint(1, len(csv_prompts))
262
+ prompt_text = csv_prompts[prompt_id - 1]
263
+
264
+ await ctx.send(f"🎲 **Random Prompt #{prompt_id}**: `{prompt_text[:100]}...`")
265
+
266
+ voiceover_text = generate_natural_voiceover(prompt_text)
267
+
268
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
269
+ await text_to_speech(voiceover_text, "en-US-JennyNeural", tmp.name)
270
+ await ctx.send(file=discord.File(tmp.name, filename=f"random_{prompt_id}.mp3"))
271
 
272
  @bot.command(name="info")
273
  async def bot_info(ctx):
274
  """Show bot information"""
275
  embed = discord.Embed(
276
  title="πŸ€– Voiceover Generator Bot",
277
+ description="Generates voiceovers from CSV in #general",
278
  color=discord.Color.blue()
279
  )
280
+ embed.add_field(name="CSV Channel", value="#general", inline=False)
281
+ embed.add_field(name="Prompts Loaded", value=len(csv_prompts) if csv_prompts else 0, inline=False)
282
+ embed.add_field(name="Voice Styles", value="\n".join([f"{k}. {v['name']}" for k, v in VOICE_STYLES.items()]), inline=False)
283
+ embed.add_field(name="Commands", value="""`!reload` - Reload CSV
284
+ `!list` - List prompts
285
  `!count` - Count prompts
286
  `!generate <id>` - Generate voiceover
287
  `!voice <id> <style>` - Generate with style
288
  `!batch <start> <end>` - Batch generate
289
+ `!random` - Random prompt
290
+ `!info` - This menu""", inline=False)
291
 
292
  await ctx.send(embed=embed)
293
 
294
+ @bot.command(name="ping")
295
+ async def ping(ctx):
296
+ """Check if bot is alive"""
297
+ await ctx.send(f"πŸ“ Pong! Latency: {round(bot.latency * 1000)}ms")
298
+
299
  # ============================================================
300
  # RUN BOT
301
  # ============================================================
302
  if __name__ == "__main__":
303
+ print("=" * 50)
304
  print("πŸ€– Discord Voiceover Bot")
305
+ print("=" * 50)
306
+ print("πŸ“ Reading CSV from #general channel")
307
  print("πŸš€ Starting bot...")
308
+ print("=" * 50)
309
 
310
  bot.run(TOKEN)