aboutsummaryrefslogtreecommitdiffstats
path: root/cogs/jarvis.py
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-02-04 16:19:22 +0100
committersalmonik <raisersoft@proton.me>2026-02-04 16:19:22 +0100
commit72abc43db8f732743c65d6f6a820369ab8665766 (patch)
tree094fafdb0baf2e06a02045b1b2c0be88931a379a /cogs/jarvis.py
parentrefactor music (diff)
downloadsenator-sperma-72abc43db8f732743c65d6f6a820369ab8665766.tar.gz
senator-sperma-72abc43db8f732743c65d6f6a820369ab8665766.zip
more asynchronous work and other minor improvements
Diffstat (limited to '')
-rw-r--r--cogs/jarvis.py218
1 files changed, 128 insertions, 90 deletions
diff --git a/cogs/jarvis.py b/cogs/jarvis.py
index 0096b38..b199c8a 100644
--- a/cogs/jarvis.py
+++ b/cogs/jarvis.py
@@ -29,126 +29,164 @@ class Jarvis(commands.Cog):
29 29
30 if message.mentions: 30 if message.mentions:
31 if message.mentions[0].id == self.bot.user.id: 31 if message.mentions[0].id == self.bot.user.id:
32 content = await self.get_original(message) 32 await self.jarvis(message)
33 await self.jarvis(message, content)
34 33
35 elif "jarvis" in message.content.lower(): 34 elif "jarvis" in message.content.lower():
36 content = await self.get_original(message) 35 await self.jarvis(message)
37 await self.jarvis(message, content)
38 36
39 @staticmethod 37 @staticmethod
40 async def jarvis(message, content): 38 async def jarvis(message):
41 url = "https://openrouter.ai/api/v1/chat/completions"
42 headers = { 39 headers = {
43 40 "Authorization": f"Bearer {os.getenv('OPENROUTER_API_KEY')}",
44 "Authorization": f"Bearer {os.getenv("OPENROUTER_API_KEY")}",
45 "Content-Type": "application/json" 41 "Content-Type": "application/json"
46 } 42 }
47 43
48 # Build messages array 44 messages = Jarvis._build_messages(message)
49 messages = [ 45 payload = Jarvis._build_payload(messages)
50 { 46
51 "role": "system", 47 response_data = await Jarvis._make_api_request(payload, headers)
52 "content": "your task is to tell the program what to do and how to respond after receiving a message.\n" 48 response_message, action = Jarvis._parse_response(response_data)
53 "message field has to be filled out in polish.\n" 49
54 "act like jarvis, be a personal assistant to user, do not use emojis\n" 50 channel = message.channel
55 "do not ask the user if they want to do an action. the user will tell you that" 51 await channel.send(response_message)
56 } 52
57 ] 53 if action == "download":
54 await Jarvis._handle_download_action(message, channel)
55
56 @staticmethod
57 def _build_messages(message):
58 messages = [{
59 "role": "system",
60 "content": "your task is to tell the program what to do and how to respond after receiving a message.\n"
61 "message field has to be filled out in polish.\n"
62 "act like jarvis, be a personal assistant to user, do not use emojis\n"
63 "do not ask the user if they want to do an action. the user will tell you that"
64 }]
58 65
59 # Add original message with display name
60 messages.append({ 66 messages.append({
61 "role": "user", 67 "role": "user",
62 "content": f"[from {message.author.display_name}]: {message.content}" 68 "content": f"[from {message.author.display_name}]: {message.content}"
63 }) 69 })
64 70
65 # If it's a reply, add the original message and reply as separate messages 71 if Jarvis._is_reply(message):
66 if message.reference and (message.type == discord.MessageType.reply or message.reference.resolved): 72 messages.extend(Jarvis._build_reply_messages(message))
67 if message.reference.resolved: 73
68 original_msg = message.reference.resolved 74 return messages
69 messages.append({ 75
70 "role": "user", 76 @staticmethod
71 "content": f"[original message from {original_msg.author.display_name}]: {original_msg.content}" 77 def _is_reply(message):
72 }) 78 return message.reference and (message.type == discord.MessageType.reply or message.reference.resolved)
73 messages.append({ 79
74 "role": "user", 80 @staticmethod
75 "content": f"[reply from {message.author.display_name}]: {message.content}" 81 def _build_reply_messages(message):
76 }) 82 original_msg = message.reference.resolved
77 83 return [
78 payload = { 84 {
85 "role": "user",
86 "content": f"[original message from {original_msg.author.display_name}]: {original_msg.content}"
87 },
88 {
89 "role": "user",
90 "content": f"[reply from {message.author.display_name}]: {message.content}"
91 }
92 ]
93
94 @staticmethod
95 def _build_payload(messages):
96 return {
79 "model": "arcee-ai/trinity-large-preview:free", 97 "model": "arcee-ai/trinity-large-preview:free",
80 "messages": messages, 98 "messages": messages,
81 "response_format": { 99 "response_format": Jarvis._get_response_schema(),
82 "type": "json_schema", 100 "plugins": [{"id": "response-healing"}]
83 "json_schema": { 101 }
84 "name": "response", 102
85 "strict": True, 103 @staticmethod
86 "schema": { 104 def _get_response_schema():
87 "type": "object", 105 return {
88 "properties": { 106 "type": "json_schema",
89 "message": { 107 "json_schema": {
90 "type": "string", 108 "name": "response",
91 "description": "the message that user will receive", 109 "strict": True,
92 }, 110 "schema": {
93 "action": { 111 "type": "object",
94 "type": "string", 112 "properties": {
95 "description": "the action to do, either download, compress or none" 113 "message": {
96 } 114 "type": "string",
115 "description": "the message that user will receive",
97 }, 116 },
98 "required": ["message", "action"], 117 "action": {
99 "additionalProperties": False, 118 "type": "string",
119 "description": "the action to do, either download, compress or none"
100 } 120 }
101 } 121 },
102 }, 122 "required": ["message", "action"],
103 "plugins": [ 123 "additionalProperties": False,
104 {"id": "response-healing"} 124 }
105 ] 125 }
106 } 126 }
107 127
128 @staticmethod
129 async def _make_api_request(payload, headers):
130 url = "https://openrouter.ai/api/v1/chat/completions"
108 response = requests.post(url=url, headers=headers, json=payload) 131 response = requests.post(url=url, headers=headers, json=payload)
109 # Parse the JSON response
110 response_data = response.json() 132 response_data = response.json()
111 logger.info(response_data) 133 logger.info(response_data)
112 # Extract the content string 134 return response_data
113 content = response_data['choices'][0]['message']['content']
114 135
115 # Remove the code block markers and parse the inner JSON 136 @staticmethod
137 def _parse_response(response_data):
138 content = response_data['choices'][0]['message']['content']
116 inner_json_str = content.strip('```json\n').strip('```').strip() 139 inner_json_str = content.strip('```json\n').strip('```').strip()
117 inner_data = json.loads(inner_json_str) 140 inner_data = json.loads(inner_json_str)
118 141
119 # Now you can access the message and action 142 response_message = inner_data['message']
120 response_message = inner_data['message'] # "Proszę podać plik do kompresji lub link do pobrania." 143 action = inner_data['action']
121 action = inner_data['action'] # "none OR download OR compress" 144
122 channel = message.channel
123 logger.info(response_message) 145 logger.info(response_message)
124 logger.info(action) 146 logger.info(action)
147
148 return response_message, action
125 149
126 await channel.send(response_message) 150 @staticmethod
151 async def _handle_download_action(message, channel):
152 url_match = Jarvis._extract_url(message.content)
153 if not url_match:
154 await channel.send("Nie znaleziono poprawnego linku w odpowiedzi.")
155 return
127 156
128 if action == "download": 157 response = await Downloader.download_standalone(url=url_match)
129 # Extract the URL from the message using regex 158 await Jarvis._process_download_response(response, channel)
130 url_match = re.search(r'https://(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]{1,63}\.[A-Za-z]{2,63}/\S*', message.content).group(0) 159
131 if not url_match: 160 @staticmethod
132 await channel.send("Nie znaleziono poprawnego linku w odpowiedzi.") 161 def _extract_url(content):
133 return 162 match = re.search(r'https://(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]{1,63}\.[A-Za-z]{2,63}/\S*', content)
134 163 return match.group(0) if match else None
135 response = await Downloader.download_standalone(url=url_match) 164
136 if isinstance(response, dict): 165 @staticmethod
137 if response["status"] == "success": 166 async def _process_download_response(response, channel):
138 if response["type"] == "url": 167 if not isinstance(response, dict):
139 await channel.send(f"pobioron i wysyłon: {response['content']}") 168 return
140 elif response["type"] == "file": 169
141 file_data = response["content"] 170 if response["status"] == "success":
142 await channel.send("wysyłon:", file=discord.File(file_data)) 171 await Jarvis._handle_successful_download(response, channel)
143 shutil.rmtree(response["temp_dir"]) 172 else:
144 else: 173 await Jarvis._handle_failed_download(response)
145 logger.error(f"Error: {response['content']}") 174
146 if 'details' in response: 175 @staticmethod
147 logger.error(f"Details: {response['details']}") 176 async def _handle_successful_download(response, channel):
148 shutil.rmtree(response["temp_dir"]) 177 if response["type"] == "url":
149 178 await channel.send(f"pobioron i wysyłon: {response['content']}")
150 elif action == "compress": 179 elif response["type"] == "file":
151 pass 180 file_data = response["content"]
181 await channel.send("wysyłon:", file=discord.File(file_data))
182 shutil.rmtree(response["temp_dir"])
183
184 @staticmethod
185 async def _handle_failed_download(response):
186 logger.error(f"Error: {response['content']}")
187 if 'details' in response:
188 logger.error(f"Details: {response['details']}")
189 shutil.rmtree(response["temp_dir"])
152 190
153 @staticmethod 191 @staticmethod
154 async def get_original(message): 192 async def get_original(message):