TIMBOVILL commited on
Commit
ff7b34a
·
verified ·
1 Parent(s): a0b76d3

Upload bot.py

Browse files
Files changed (1) hide show
  1. bot.py +434 -0
bot.py ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import discord
5
+ from discord.ext import commands
6
+ from dotenv import load_dotenv
7
+ from groq import Groq
8
+ from collections import defaultdict
9
+ import requests
10
+ import json
11
+ from datetime import datetime, timedelta
12
+ import google.generativeai as gemini
13
+ import asyncio
14
+ import subprocess
15
+ # Load environment variables from .env file
16
+ load_dotenv()
17
+ DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
18
+ GROQ_API_KEY = os.getenv('GROQ_API_KEY')
19
+ GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
20
+ WHISPER_CPP_PATH = os.getenv('WHISPER_CPP_PATH') # Path to the whisper.cpp executable
21
+ WHISPER_MODEL = os.getenv('WHISPER_CPP_MODEL') # Specify the Whisper model you want to use
22
+ # Initialize Groq client
23
+ client = Groq(api_key=GROQ_API_KEY)
24
+
25
+ # Initialize the Google Generative AI client
26
+ gemini.configure(api_key=GOOGLE_API_KEY)
27
+
28
+ # Initialize the bot with intents
29
+ intents = discord.Intents.default()
30
+ intents.messages = True
31
+ intents.message_content = True
32
+ bot = commands.Bot(command_prefix='!', intents=intents)
33
+
34
+ # Authorized users/roles (replace with actual IDs)
35
+ authorized_users = [936673139419664414] # Replace with user IDs
36
+ authorized_roles = [1198707036070871102] # Replace with role IDs
37
+
38
+ # Bot-wide settings
39
+ bot_settings = {
40
+ "model": "llama3-70b-8192",
41
+ "system_prompt": "You are a helpful and friendly AI assistant.",
42
+ "context_messages": 5,
43
+ "llm_enabled": False # LLM is enabled by default for the entire bot
44
+ }
45
+
46
+ # Define valid model names for Groq and Gemini
47
+ groq_models = [
48
+ "llama3-70b-8192",
49
+ "llama3-8b-8192",
50
+ "gemma-7b-it",
51
+ "mixtral-8x7b-32768"
52
+ ]
53
+ # Define valid model names for Gemini
54
+ gemini_models = [
55
+ "gemini-1.5-flash",
56
+ "gemini-1.5-pro-latest"
57
+ ]
58
+
59
+ # Bot-wide settings
60
+ bot_settings = {
61
+ "model": "llama3-8b-8192",
62
+ "system_prompt": "You are a helpful and friendly AI assistant.",
63
+ "context_messages": 5,
64
+ "llm_enabled": False # Start with LLMs off
65
+ }
66
+
67
+ # --- Conversation Data (Important!) chatting shit
68
+ conversation_data = defaultdict(lambda: {"messages": []})
69
+
70
+
71
+ # --- Helper Function for Checking Authorization ---
72
+
73
+ def is_authorized(interaction: discord.Interaction):
74
+ """Check if the user has permission to use the command."""
75
+ user = interaction.user
76
+ return user.id in authorized_users or any(role.id in authorized_roles for role in user.roles)
77
+
78
+ # --- Application Commands ---
79
+
80
+ @bot.tree.command(name="serverinfo", description="Get information about the server.")
81
+ async def serverinfo(interaction: discord.Interaction):
82
+ if not is_authorized(interaction):
83
+ await interaction.response.send_message("You are not authorized to use this command.", ephemeral=True)
84
+ return
85
+
86
+ embed = discord.Embed(
87
+ title="Sanctuary: Open Source AI",
88
+ description="Sanctuary is a Discord server dedicated to open-source AI projects and research. It's a place for users, developers, and researchers to connect, share their work, help each other and collaborate. The server aims to highlight amazing open-source projects and inspire developers to push the boundaries of AI.",
89
+ color=discord.Color.blue()
90
+ )
91
+ embed.add_field(
92
+ name="How to Help",
93
+ value="1. Boost the server to unlock more features.\n2. Spread the word to your friends.\n3. Help improve the server by posting suggestions in the designated channel.",
94
+ inline=False
95
+ )
96
+ embed.add_field(name="Permanent Invite Link", value="[Join Sanctuary](https://discord.gg/kSaydjBXwf)", inline=False)
97
+ await interaction.response.send_message(embed=embed)
98
+
99
+
100
+ @bot.tree.command(name="set_model", description="Set the language model for the entire bot.")
101
+ async def set_model(interaction: discord.Interaction, model_name: str):
102
+ if not is_authorized(interaction):
103
+ await interaction.response.send_message("You are not authorized to use this command.", ephemeral=True)
104
+ return
105
+
106
+ model_name = model_name.lower()
107
+ if model_name in groq_models:
108
+ bot_settings["model"] = model_name
109
+ await interaction.response.send_message(f"Model set to: **{model_name}** for the entire bot.")
110
+ elif model_name in gemini_models:
111
+ bot_settings["model"] = model_name
112
+ await interaction.response.send_message(f"Model set to: **Google Gemini {model_name}** for the entire bot.")
113
+ else:
114
+ await interaction.response.send_message(f"Invalid model.\nAvailable models:\nGroq: {', '.join(groq_models)}\nGemini: {', '.join(gemini_models)}")
115
+
116
+ @bot.tree.command(name="search_github_projects", description="Search for GitHub projects.")
117
+ async def search_github_projects(interaction: discord.Interaction, query: str):
118
+ """Search for GitHub projects based on a search query.
119
+
120
+ Args:
121
+ query: The GitHub search query (e.g., 'machine learning', 'topic:natural-language-processing').
122
+ """
123
+ try:
124
+ # Search for repositories
125
+ url = "https://api.github.com/search/repositories"
126
+ params = {
127
+ "q": query,
128
+ "sort": "stars",
129
+ "order": "desc",
130
+ "per_page": 1 # Get top 5 matching repos
131
+ }
132
+
133
+ response = requests.get(url, params=params)
134
+ response.raise_for_status()
135
+
136
+ matching_repos = response.json()["items"]
137
+
138
+ if matching_repos:
139
+ embed = discord.Embed(
140
+ title=f"GitHub Project Search Results for: {query}",
141
+ color=discord.Color.green() # Use a different color for search
142
+ )
143
+
144
+ for repo in matching_repos:
145
+ repo_name = repo['name']
146
+ repo_url = repo['html_url']
147
+ description = repo['description'] or "No description."
148
+
149
+ embed.add_field(
150
+ name=f"{repo_name}",
151
+ value=f"**[Link to Repo]({repo_url})**\n{description}\n"
152
+ f"⭐ {repo['stargazers_count']} "
153
+ f"🍴 {repo['forks_count']}",
154
+ inline=False
155
+ )
156
+
157
+ await interaction.response.send_message(embed=embed)
158
+ else:
159
+ await interaction.response.send_message(f"No projects found for query: {query}")
160
+
161
+ except requests.exceptions.RequestException as e:
162
+ await interaction.response.send_message(f"An error occurred while searching GitHub: {e}")
163
+
164
+ @bot.tree.command(name="help", description="Show available commands.")
165
+ async def help_command(interaction: discord.Interaction):
166
+ embed = discord.Embed(title="Available Commands", color=discord.Color.blue())
167
+
168
+ embed.add_field(name="/serverinfo", value="Gives info about the server.", inline=False)
169
+ embed.add_field(name="/set_model <model_name>", value="Set the language model for the entire bot. (ADMIN)", inline=False)
170
+ embed.add_field(name="/set_system_prompt <prompt>", value="Set the system prompt for the entire bot. (ADMIN)", inline=False)
171
+ embed.add_field(name="/set_context_messages <num_messages>", value="Set the number of context messages to use (1-10) for the entire bot. (ADMIN)", inline=False)
172
+ embed.add_field(name="/say <message>", value="Make the bot say something. (ADMIN)", inline=False)
173
+ embed.add_field(name="/toggle_llm", value="Turn the LLM part of the bot on or off for the entire bot. (ADMIN)", inline=False)
174
+ embed.add_field(name="/trending_projects <query>", value="Show trending GitHub projects (past 7 days). Default query: 'topic:language-model'.", inline=False)
175
+ embed.add_field(name="/search_github_projects <query>", value="Search for GitHub projects.", inline=False)
176
+ embed.add_field(name="/summarize <prompt>", value="Summarize info given.", inline=False)
177
+ await interaction.response.send_message(embed=embed)
178
+
179
+ @bot.tree.command(name="trending_projects", description="Show trending GitHub projects.")
180
+ async def trending_projects(interaction: discord.Interaction, query: str = "topic:language-model"):
181
+ """Show trending GitHub projects based on a search query.
182
+
183
+ Args:
184
+ query: The GitHub search query (e.g., 'topic:machine-learning').
185
+ Defaults to 'topic:language-model'.
186
+ """
187
+ try:
188
+ # Get trending repositories
189
+ url = "https://api.github.com/search/repositories"
190
+ date_threshold = (datetime.utcnow() - timedelta(days=7)).strftime("%Y-%m-%d")
191
+ params = {
192
+ "q": f"{query} created:>{date_threshold}",
193
+ "sort": "stars",
194
+ "order": "desc",
195
+ "per_page": 5
196
+ }
197
+
198
+ response = requests.get(url, params=params)
199
+ response.raise_for_status()
200
+
201
+ trending_repos = response.json()["items"]
202
+
203
+ if trending_repos:
204
+ embed = discord.Embed(
205
+ title=f"Trending GitHub Projects for Query: {query} (Past 7 Days)",
206
+ color=discord.Color.blue()
207
+ )
208
+
209
+ for repo in trending_repos:
210
+ repo_name = repo['name']
211
+ repo_url = repo['html_url']
212
+ description = repo['description'] or "No description."
213
+
214
+ # Create the field value with the link SEPARATELY:
215
+ field_value = f"{description}\n"
216
+ field_value += f"⭐ {repo['stargazers_count']} "
217
+ field_value += f"🍴 {repo['forks_count']}"
218
+
219
+ # Add the field with the name as the link:
220
+ embed.add_field(
221
+ name=f"{repo_name}", # Only the repo name here, no bolding or linking
222
+ value=f"**[Link to Repo]({repo_url})**\n{description}\n"
223
+ f"⭐ {repo['stargazers_count']} "
224
+ f"🍴 {repo['forks_count']}",
225
+ inline=False
226
+ )
227
+
228
+ await interaction.response.send_message(embed=embed)
229
+ else:
230
+ await interaction.response.send_message(f"No trending projects found for query: {query}")
231
+
232
+ except requests.exceptions.RequestException as e:
233
+ await interaction.response.send_message(f"An error occurred while fetching data from GitHub: {e}")
234
+
235
+ @bot.tree.command(name="set_system_prompt", description="Set the system prompt for the entire bot.")
236
+ async def set_system_prompt(interaction: discord.Interaction, prompt: str):
237
+ if not is_authorized(interaction):
238
+ await interaction.response.send_message("You are not authorized to use this command.", ephemeral=True)
239
+ return
240
+
241
+ bot_settings["system_prompt"] = prompt
242
+ await interaction.response.send_message(f"System prompt set to:\n```\n{prompt}\n``` for the entire bot.")
243
+
244
+
245
+ @bot.tree.command(name="summarize", description="Summarize a text using the current LLM.")
246
+ async def summarize(interaction: discord.Interaction, text: str):
247
+
248
+ try:
249
+ selected_model = bot_settings["model"]
250
+
251
+ if selected_model in gemini_models:
252
+ try:
253
+ # Create a Gemini model instance (do this once, maybe outside the function)
254
+ gemini_model = gemini.GenerativeModel(selected_model)
255
+
256
+ # Use the model instance to generate content
257
+ response = gemini_model.generate_content(
258
+ f"Summarize the following text:\n\n{text}",
259
+ )
260
+
261
+ # Extract the summary from the response
262
+ summary = response.text
263
+ await interaction.response.send_message(f"Summary:\n```\n{summary}\n```")
264
+ except Exception as e:
265
+ await interaction.response.send_message(f"An error occurred while processing the request: {e}")
266
+
267
+ else: # Use Groq API for summarization
268
+ system_prompt = bot_settings["system_prompt"]
269
+ chat_completion = client.chat.completions.create(
270
+ messages=[
271
+ {"role": "system", "content": system_prompt},
272
+ {"role": "user", "content": f"Summarize the following text:\n\n{text}"}
273
+ ],
274
+ model=selected_model
275
+ )
276
+ summary = chat_completion.choices[0].message.content
277
+ await interaction.response.send_message(f"Summary:\n```\n{summary}\n```")
278
+
279
+ except Exception as e:
280
+ await interaction.response.send_message(f"An error occurred: {e}")
281
+
282
+
283
+ @bot.tree.command(name="play_audio", description="Join a voice channel and play audio. (Authorized users only)")
284
+ async def play_audio(interaction: discord.Interaction, channel: discord.VoiceChannel):
285
+ """Joins a specified voice channel and plays an audio file.
286
+
287
+ Args:
288
+ channel: The voice channel to join.
289
+ """
290
+
291
+ if not is_authorized(interaction):
292
+ await interaction.response.send_message(
293
+ "You are not authorized to use this command.", ephemeral=True
294
+ )
295
+ return
296
+
297
+ # Check if the bot is already connected to a voice channel
298
+ if interaction.guild.voice_client:
299
+ await interaction.response.send_message(
300
+ "The bot is already connected to a voice channel.", ephemeral=True
301
+ )
302
+ return
303
+
304
+ try:
305
+ # Connect to the specified voice channel
306
+ await channel.connect()
307
+ await interaction.response.send_message(
308
+ f"Connected to voice channel: {channel.name}", ephemeral=True
309
+ )
310
+
311
+ # Path to your audio file (replace with the actual path)
312
+ audio_file = r"path to audio shit"
313
+
314
+ # Create a StreamPlayer for the audio
315
+ source = discord.FFmpegPCMAudio(audio_file)
316
+ interaction.guild.voice_client.play(source)
317
+
318
+ # Wait until the audio finishes playing
319
+ while interaction.guild.voice_client.is_playing():
320
+ await asyncio.sleep(1)
321
+
322
+ # Disconnect from the voice channel
323
+ await interaction.guild.voice_client.disconnect()
324
+
325
+ except Exception as e:
326
+ await interaction.response.send_message(
327
+ f"An error occurred: {e}", ephemeral=True
328
+ )
329
+
330
+
331
+ @bot.tree.command(name="set_context_messages", description="Set the number of context messages to use (1-10) for the entire bot.")
332
+ async def set_context_messages(interaction: discord.Interaction, num_messages: int):
333
+ if not is_authorized(interaction):
334
+ await interaction.response.send_message("You are not authorized to use this command.", ephemeral=True)
335
+ return
336
+
337
+ if 1 <= num_messages <= 10:
338
+ bot_settings["context_messages"] = num_messages
339
+ await interaction.response.send_message(f"Number of context messages set to: {num_messages} for the entire bot.")
340
+ else:
341
+ await interaction.response.send_message("Invalid number of messages. Please choose between 1 and 10.")
342
+
343
+ @bot.tree.command(name="say", description="Make the bot say something.")
344
+ async def say(interaction: discord.Interaction, message: str):
345
+ if not is_authorized(interaction):
346
+ await interaction.response.send_message("You are not authorized to use this command.", ephemeral=True)
347
+ return
348
+
349
+ # Delete the user's command invocation (it will briefly appear)
350
+ await interaction.response.defer(ephemeral=True)
351
+ await interaction.delete_original_response()
352
+
353
+ # Send the message as the bot
354
+ await interaction.channel.send(message)
355
+
356
+
357
+ @bot.tree.command(name="toggle_llm", description="Turn the LLM part of the bot on or off for the entire bot.")
358
+ async def toggle_llm(interaction: discord.Interaction):
359
+ if not is_authorized(interaction):
360
+ await interaction.response.send_message("You are not authorized to use this command.", ephemeral=True)
361
+ return
362
+
363
+ bot_settings["llm_enabled"] = not bot_settings["llm_enabled"]
364
+ new_state = "OFF" if not bot_settings["llm_enabled"] else "ON"
365
+ await interaction.response.send_message(f"LLM is now turned {new_state} for the entire bot.")
366
+
367
+ # --- Message Handling ---
368
+
369
+ @bot.event
370
+ async def on_message(message):
371
+ await bot.process_commands(message)
372
+
373
+ if message.author == bot.user or not bot_settings["llm_enabled"]:
374
+ return
375
+
376
+ is_mentioned = bot.user.mentioned_in(message)
377
+ is_reply_to_bot = message.reference is not None and message.reference.resolved.author == bot.user
378
+
379
+ if is_mentioned or is_reply_to_bot:
380
+ try:
381
+ channel_id = str(message.channel.id)
382
+ messages = conversation_data[channel_id]["messages"]
383
+ selected_model = bot_settings["model"]
384
+ system_prompt = bot_settings["system_prompt"]
385
+ context_messages_num = bot_settings["context_messages"]
386
+
387
+ context_messages = messages[-context_messages_num:]
388
+ api_messages = [{"role": "system", "content": system_prompt}] + context_messages + [{"role": "user", "content": message.content}]
389
+
390
+ if selected_model in gemini_models:
391
+ gemini_model_mapping = {
392
+ "flash": "gemini-1.5-pro-flash",
393
+ "pro": "gemini-1.5-pro-latest"
394
+ }
395
+ model_id = gemini_model_mapping.get(selected_model, "gemini-1.5-pro-latest")
396
+
397
+ url = f'https://generativelanguage.googleapis.com/v1beta/models/{model_id}:generateContent?key={GOOGLE_API_KEY}'
398
+ headers = {'Content-Type': 'application/json'}
399
+ data = {"contents": [{"parts": [{"text": m["content"]}]}] for m in api_messages}
400
+
401
+ response = requests.post(url, headers=headers, data=json.dumps(data))
402
+
403
+ if response.status_code == 200:
404
+ response_json = response.json()
405
+ generated_text = response_json['candidates'][0]['content']['parts'][0]['text']
406
+ else:
407
+ await message.channel.send(f"Error: {response.status_code}\n{response.text}")
408
+ return
409
+ else: # Groq model
410
+ chat_completion = client.chat.completions.create(
411
+ messages=api_messages,
412
+ model=selected_model
413
+ )
414
+ generated_text = chat_completion.choices[0].message.content
415
+
416
+ await message.channel.send(generated_text.strip())
417
+
418
+ messages.append({"role": "user", "content": message.content})
419
+ messages.append({"role": "assistant", "content": generated_text.strip()})
420
+ conversation_data[channel_id]["messages"] = messages[-10:]
421
+
422
+ except Exception as e:
423
+ await message.channel.send(f"An error occurred: {e}")
424
+
425
+ # --- Event Handling ---
426
+
427
+ @bot.event
428
+ async def on_ready():
429
+ print(f'Logged in as {bot.user.name}')
430
+ await bot.tree.sync(guild=None)
431
+ print("Application commands synced.")
432
+
433
+ # Run the bot
434
+ bot.run(DISCORD_TOKEN)