aboutsummaryrefslogtreecommitdiffstats
path: root/cogs/jarvis.py
diff options
context:
space:
mode:
Diffstat (limited to 'cogs/jarvis.py')
-rw-r--r--cogs/jarvis.py198
1 files changed, 0 insertions, 198 deletions
diff --git a/cogs/jarvis.py b/cogs/jarvis.py
deleted file mode 100644
index 866700e..0000000
--- a/cogs/jarvis.py
+++ /dev/null
@@ -1,198 +0,0 @@
1# This file is part of Senator Sperma
2# Licensed under the GPL-3.0 License
3# Copyright (C) 2026 Salmonik @ salmonik@tuta.io
4# https://codeberg.org/salmonik/senator-sperma
5
6import re
7import discord
8from discord.ext import commands
9import requests
10import json
11import logging
12import shutil
13from dotenv import load_dotenv
14import os
15from tempfile import NamedTemporaryFile
16from cogs.downloader import Downloader
17
18logger = logging.getLogger(__name__)
19load_dotenv()
20
21class Jarvis(commands.Cog):
22 def __init__(self, bot):
23 self.bot = bot
24
25 @commands.Cog.listener()
26 async def on_message(self, message):
27 if message.author == self.bot.user:
28 return
29
30 logger.info(message.content)
31 if message.mentions:
32 if message.mentions[0].id == self.bot.user.id:
33 await self.jarvis(message)
34
35 elif "jarvis" in message.content.lower():
36 await self.jarvis(message)
37
38 @staticmethod
39 async def jarvis(message):
40 headers = {
41 "Authorization": f"Bearer {os.getenv('OPENROUTER_API_KEY')}",
42 "Content-Type": "application/json"
43 }
44
45 chain = await Jarvis._build_message_chain(message)
46 messages = Jarvis._build_messages(message, chain)
47 payload = Jarvis._build_payload(messages)
48
49 response_data = await Jarvis._make_api_request(payload, headers)
50 response_message, action = Jarvis._parse_response(response_data)
51
52 channel = message.channel
53 await message.reply(response_message)
54
55 if action == "download":
56 await Jarvis._handle_download_action(message, channel)
57
58 @staticmethod
59 async def _build_message_chain(message):
60 chain = []
61 current = message
62 max_depth = 5
63
64 while current and len(chain) < max_depth:
65 chain.append(current)
66 if not current.reference:
67 break
68 try:
69 if current.reference.resolved:
70 current = current.reference.resolved
71 else:
72 current = await current.channel.fetch_message(current.reference.message_id)
73 except discord.NotFound:
74 break
75
76 return list(reversed(chain))
77
78 @staticmethod
79 def _build_messages(message, chain=None):
80 messages = [{
81 "role": "system",
82 "content": "Jesteś Senator Sperma Armostrong, także znany jako Jarvis."
83 "Twoim zadaniem, po za asystowaniem użytkownika, jest mówienie programowi nadrzędnemu jak sie zachować"
84 "pole 'message' może być tylko wypełnione w języku Polskim."
85 "zachowuj sie jak Jarvis, bądź osobistym asystentem użytkownika, nie używaj emotikon"
86 "nie pytaj sie użytkownika czy chcesz abyś coś zrobił, użytkownik ci sam powie abyś coś wykonał"
87 }]
88
89 if chain is None:
90 chain = [message]
91
92 for msg in chain:
93 messages.append({
94 "role": "user",
95 "content": f"{msg.author.display_name}: {msg.content}"
96 })
97
98 return messages
99
100 @staticmethod
101 def _build_payload(messages):
102 return {
103 "model": "arcee-ai/trinity-large-preview:free",
104 "messages": messages,
105 "response_format": Jarvis._get_response_schema(),
106 "plugins": [{"id": "response-healing"}]
107 }
108
109 @staticmethod
110 def _get_response_schema():
111 return {
112 "type": "json_schema",
113 "json_schema": {
114 "name": "response",
115 "strict": True,
116 "schema": {
117 "type": "object",
118 "properties": {
119 "message": {
120 "type": "string",
121 "description": "the message that user will receive",
122 },
123 "action": {
124 "type": "string",
125 "description": "the action to do, either download, compress or none"
126 }
127 },
128 "required": ["message", "action"],
129 "additionalProperties": False,
130 }
131 }
132 }
133
134 @staticmethod
135 async def _make_api_request(payload, headers):
136 url = "https://openrouter.ai/api/v1/chat/completions"
137 response = requests.post(url=url, headers=headers, json=payload)
138 response_data = response.json()
139 logger.info(response_data)
140 return response_data
141
142 @staticmethod
143 def _parse_response(response_data):
144 content = response_data['choices'][0]['message']['content']
145 inner_json_str = content.strip('```json\n').strip('```').strip()
146 inner_data = json.loads(inner_json_str)
147
148 response_message = inner_data['message']
149 action = inner_data['action']
150
151 logger.info(response_message)
152 logger.info(action)
153
154 return response_message, action
155
156 @staticmethod
157 async def _handle_download_action(message, channel):
158 url_match = Jarvis._extract_url(message.content)
159 if not url_match:
160 await channel.send("Nie znaleziono poprawnego linku w odpowiedzi.")
161 return
162
163 response = await Downloader.download_standalone(url=url_match)
164 await Jarvis._process_download_response(response, channel)
165
166 @staticmethod
167 def _extract_url(content):
168 match = re.search(r'https://(?:[A-Za-z0-9-]+\.)*[A-Za-z0-9-]{1,63}\.[A-Za-z]{2,63}/\S*', content)
169 return match.group(0) if match else None
170
171 @staticmethod
172 async def _process_download_response(response, channel):
173 if not isinstance(response, dict):
174 return
175
176 if response["status"] == "success":
177 await Jarvis._handle_successful_download(response, channel)
178 else:
179 await Jarvis._handle_failed_download(response)
180
181 @staticmethod
182 async def _handle_successful_download(response, channel):
183 if response["type"] == "url":
184 await channel.send(f"pobioron i wysyłon: {response['content']}")
185 elif response["type"] == "file":
186 file_data = response["content"]
187 await channel.send("wysyłon:", file=discord.File(file_data))
188 shutil.rmtree(response["temp_dir"])
189
190 @staticmethod
191 async def _handle_failed_download(response):
192 logger.error(f"Error: {response['content']}")
193 if 'details' in response:
194 logger.error(f"Details: {response['details']}")
195 shutil.rmtree(response["temp_dir"])
196
197def setup(bot):
198 bot.add_cog(Jarvis(bot))