chrxmaticc-copilot 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/copilot.js +72 -0
- package/executables/README.md +31 -0
- package/executables/build-executables.sh +22 -0
- package/executables/chrxmaticc-copilot-linux +0 -0
- package/executables/chrxmaticc-copilot-macos +0 -0
- package/executables/chrxmaticc-copilot-win.exe +0 -0
- package/executables/virustotal-report.png +0 -0
- package/package.json +34 -0
- package/plugins/crypto.js +23 -0
- package/plugins/focus.js +56 -0
- package/plugins/github.js +31 -0
- package/plugins/translate.js +39 -0
- package/plugins/weather.js +17 -0
- package/src/apis/groq.js +77 -0
- package/src/apis/pollinations.js +60 -0
- package/src/apis/server.js +126 -0
- package/src/chat.js +480 -0
- package/src/extensions/clipboard-watcher.js +66 -0
- package/src/extensions/code-review.js +63 -0
- package/src/extensions/spotify.js +357 -0
- package/src/extensions/terminal-hook.js +56 -0
- package/src/memory.js +57 -0
- package/src/multi-user-chat.js +139 -0
- package/src/personality.js +120 -0
- package/src/plugin-engine.js +132 -0
- package/src/stt.js +85 -0
- package/src/tts.js +72 -0
- package/src/voice.js +65 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
var chalk = require('chalk');
|
|
2
|
+
// Chrxmaticc Copilot — Spotify Integration v2.0
|
|
3
|
+
// Also, all Spotify Logins and Credentials are private. Please know this.
|
|
4
|
+
// Full account login, playlist control, music analysis
|
|
5
|
+
// Author: Chrxmee-Midnightt
|
|
6
|
+
|
|
7
|
+
var chat = require('../chat');
|
|
8
|
+
var chalk = require('chalk');
|
|
9
|
+
var https = require('https');
|
|
10
|
+
|
|
11
|
+
// Spotify API credentials (registered app)
|
|
12
|
+
var SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID || '';
|
|
13
|
+
var SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET || '';
|
|
14
|
+
var SPOTIFY_REDIRECT_URI = process.env.SPOTIFY_REDIRECT_URI || 'http://localhost:3000/spotify/callback';
|
|
15
|
+
|
|
16
|
+
var userSessions = {};
|
|
17
|
+
|
|
18
|
+
// ──────────────────────────────────────────────
|
|
19
|
+
// SECURITY DISCLAIMER
|
|
20
|
+
// ──────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
var SECURITY_DISCLAIMER = [
|
|
23
|
+
'🔒 SECURITY NOTICE:',
|
|
24
|
+
'• Chrxmaticc Copilot does NOT store your password.',
|
|
25
|
+
'• Credentials are sent directly to Spotify via OAuth.',
|
|
26
|
+
'• Your data is private and never logged.',
|
|
27
|
+
'• 2FA codes are processed in real-time only.',
|
|
28
|
+
'• You can use public playlists without logging in.',
|
|
29
|
+
'• Account login enables: private playlists, liked songs, playback control.',
|
|
30
|
+
'• Log out anytime with /spotify logout.',
|
|
31
|
+
'• This is an open-source project. Verify the code yourself.',
|
|
32
|
+
'• By logging in, you agree to Spotify\'s Terms of Service.'
|
|
33
|
+
].join('\n');
|
|
34
|
+
|
|
35
|
+
function showDisclaimer() {
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log(' ' + chalk.yellow(SECURITY_DISCLAIMER));
|
|
38
|
+
console.log('');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ──────────────────────────────────────────────
|
|
42
|
+
// AUTHENTICATION
|
|
43
|
+
// ──────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
function getAuthURL() {
|
|
46
|
+
var params = new URLSearchParams({
|
|
47
|
+
client_id: SPOTIFY_CLIENT_ID,
|
|
48
|
+
response_type: 'code',
|
|
49
|
+
redirect_uri: SPOTIFY_REDIRECT_URI,
|
|
50
|
+
scope: 'user-read-private user-read-email playlist-read-private playlist-modify-private playlist-modify-public user-library-read user-library-modify user-read-playback-state user-modify-playback-state',
|
|
51
|
+
state: Math.random().toString(36).substring(7)
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return 'https://accounts.spotify.com/authorize?' + params.toString();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function exchangeCodeForToken(code, userId) {
|
|
58
|
+
return new Promise(function(resolve, reject) {
|
|
59
|
+
var data = new URLSearchParams({
|
|
60
|
+
grant_type: 'authorization_code',
|
|
61
|
+
code: code,
|
|
62
|
+
redirect_uri: SPOTIFY_REDIRECT_URI,
|
|
63
|
+
client_id: SPOTIFY_CLIENT_ID,
|
|
64
|
+
client_secret: SPOTIFY_CLIENT_SECRET
|
|
65
|
+
}).toString();
|
|
66
|
+
|
|
67
|
+
var options = {
|
|
68
|
+
hostname: 'accounts.spotify.com',
|
|
69
|
+
path: '/api/token',
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
73
|
+
'Content-Length': data.length
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
var req = https.request(options, function(res) {
|
|
78
|
+
var body = '';
|
|
79
|
+
res.on('data', function(chunk) { body = body + chunk; });
|
|
80
|
+
res.on('end', function() {
|
|
81
|
+
try {
|
|
82
|
+
var json = JSON.parse(body);
|
|
83
|
+
if (json.access_token) {
|
|
84
|
+
userSessions[userId] = {
|
|
85
|
+
accessToken: json.access_token,
|
|
86
|
+
refreshToken: json.refresh_token,
|
|
87
|
+
expiresAt: Date.now() + (json.expires_in * 1000),
|
|
88
|
+
createdAt: Date.now()
|
|
89
|
+
};
|
|
90
|
+
resolve({ success: true });
|
|
91
|
+
} else {
|
|
92
|
+
resolve({ success: false, error: json.error_description || 'Auth failed' });
|
|
93
|
+
}
|
|
94
|
+
} catch (e) {
|
|
95
|
+
resolve({ success: false, error: e.message });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
req.on('error', function(e) { reject(e); });
|
|
101
|
+
req.write(data);
|
|
102
|
+
req.end();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function refreshToken(userId) {
|
|
107
|
+
return new Promise(function(resolve) {
|
|
108
|
+
var session = userSessions[userId];
|
|
109
|
+
if (!session || !session.refreshToken) {
|
|
110
|
+
resolve({ success: false, error: 'No session' });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var data = new URLSearchParams({
|
|
115
|
+
grant_type: 'refresh_token',
|
|
116
|
+
refresh_token: session.refreshToken,
|
|
117
|
+
client_id: SPOTIFY_CLIENT_ID,
|
|
118
|
+
client_secret: SPOTIFY_CLIENT_SECRET
|
|
119
|
+
}).toString();
|
|
120
|
+
|
|
121
|
+
var options = {
|
|
122
|
+
hostname: 'accounts.spotify.com',
|
|
123
|
+
path: '/api/token',
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
var req = https.request(options, function(res) {
|
|
129
|
+
var body = '';
|
|
130
|
+
res.on('data', function(chunk) { body = body + chunk; });
|
|
131
|
+
res.on('end', function() {
|
|
132
|
+
try {
|
|
133
|
+
var json = JSON.parse(body);
|
|
134
|
+
if (json.access_token) {
|
|
135
|
+
userSessions[userId].accessToken = json.access_token;
|
|
136
|
+
userSessions[userId].expiresAt = Date.now() + (json.expires_in * 1000);
|
|
137
|
+
resolve({ success: true });
|
|
138
|
+
} else {
|
|
139
|
+
resolve({ success: false });
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
resolve({ success: false });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
req.on('error', function() { resolve({ success: false }); });
|
|
148
|
+
req.write(data);
|
|
149
|
+
req.end();
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function isAuthenticated(userId) {
|
|
154
|
+
var session = userSessions[userId];
|
|
155
|
+
if (!session) return false;
|
|
156
|
+
if (Date.now() > session.expiresAt) {
|
|
157
|
+
refreshToken(userId);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ──────────────────────────────────────────────
|
|
164
|
+
// MUSIC CONTROLS
|
|
165
|
+
// ──────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
function apiCall(endpoint, method, userId, body) {
|
|
168
|
+
return new Promise(function(resolve) {
|
|
169
|
+
var session = userSessions[userId];
|
|
170
|
+
if (!session) {
|
|
171
|
+
resolve({ success: false, error: 'Not authenticated' });
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
var data = body ? JSON.stringify(body) : null;
|
|
176
|
+
var options = {
|
|
177
|
+
hostname: 'api.spotify.com',
|
|
178
|
+
path: '/v1' + endpoint,
|
|
179
|
+
method: method || 'GET',
|
|
180
|
+
headers: {
|
|
181
|
+
'Authorization': 'Bearer ' + session.accessToken,
|
|
182
|
+
'Content-Type': 'application/json'
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
var req = https.request(options, function(res) {
|
|
187
|
+
var responseBody = '';
|
|
188
|
+
res.on('data', function(chunk) { responseBody = responseBody + chunk; });
|
|
189
|
+
res.on('end', function() {
|
|
190
|
+
try {
|
|
191
|
+
resolve({ success: true, data: JSON.parse(responseBody) });
|
|
192
|
+
} catch (e) {
|
|
193
|
+
resolve({ success: false, error: 'Parse error' });
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
req.on('error', function(e) { resolve({ success: false, error: e.message }); });
|
|
199
|
+
if (data) req.write(data);
|
|
200
|
+
req.end();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ──────────────────────────────────────────────
|
|
205
|
+
// PUBLIC API (no login needed)
|
|
206
|
+
// ──────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
async function searchTrack(query) {
|
|
209
|
+
var prompt = 'Search Spotify for "' + query + '". What are the top 3 results? Give song name and artist.';
|
|
210
|
+
var response = await chat.getResponse(prompt);
|
|
211
|
+
return typeof response === 'string' ? response : response.text;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function analyzeTrack(trackName, artist) {
|
|
215
|
+
var prompt = 'Analyze "' + trackName + '" by ' + artist + '. Genre, mood, what makes it unique.';
|
|
216
|
+
var response = await chat.getResponse(prompt);
|
|
217
|
+
return typeof response === 'string' ? response : response.text;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function suggestSimilar(trackName, artist) {
|
|
221
|
+
var prompt = 'Suggest 5 songs similar to "' + trackName + '" by ' + artist + '.';
|
|
222
|
+
var response = await chat.getResponse(prompt);
|
|
223
|
+
return typeof response === 'string' ? response : response.text;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function createPlaylistPrompt(mood, genre) {
|
|
227
|
+
var prompt = 'Create a 10-song playlist for a ' + mood + ' mood';
|
|
228
|
+
if (genre) prompt = prompt + ' in ' + genre;
|
|
229
|
+
prompt = prompt + '. Give song names and artists.';
|
|
230
|
+
var response = await chat.getResponse(prompt);
|
|
231
|
+
return typeof response === 'string' ? response : response.text;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ──────────────────────────────────────────────
|
|
235
|
+
// PRIVATE API (login required)
|
|
236
|
+
// ──────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
async function getMyPlaylists(userId) {
|
|
239
|
+
if (!isAuthenticated(userId)) return { success: false, error: 'Not logged in. Use /spotify login' };
|
|
240
|
+
return apiCall('/me/playlists', 'GET', userId);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function getMyLikedSongs(userId) {
|
|
244
|
+
if (!isAuthenticated(userId)) return { success: false, error: 'Not logged in' };
|
|
245
|
+
return apiCall('/me/tracks?limit=20', 'GET', userId);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function createPlaylist(userId, name, description) {
|
|
249
|
+
if (!isAuthenticated(userId)) return { success: false, error: 'Not logged in' };
|
|
250
|
+
|
|
251
|
+
var userResult = await apiCall('/me', 'GET', userId);
|
|
252
|
+
if (!userResult.success) return userResult;
|
|
253
|
+
|
|
254
|
+
return apiCall('/users/' + userResult.data.id + '/playlists', 'POST', userId, {
|
|
255
|
+
name: name,
|
|
256
|
+
description: description || 'Created by Chrxmaticc Copilot',
|
|
257
|
+
public: false
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function getCurrentPlaying(userId) {
|
|
262
|
+
if (!isAuthenticated(userId)) return { success: false, error: 'Not logged in' };
|
|
263
|
+
return apiCall('/me/player/currently-playing', 'GET', userId);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function logout(userId) {
|
|
267
|
+
delete userSessions[userId];
|
|
268
|
+
return { success: true, message: 'Logged out. Your data was never stored.' };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ──────────────────────────────────────────────
|
|
272
|
+
// COMMAND HANDLER
|
|
273
|
+
// ──────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
async function handleCommand(command, userId) {
|
|
276
|
+
var parts = command.split(' ');
|
|
277
|
+
var action = parts[1] || '';
|
|
278
|
+
var args = parts.slice(2).join(' ');
|
|
279
|
+
|
|
280
|
+
switch (action) {
|
|
281
|
+
case 'login':
|
|
282
|
+
showDisclaimer();
|
|
283
|
+
return {
|
|
284
|
+
text: '🔗 Open this URL to log in:\n' + getAuthURL() + '\n\nAfter logging in, copy the URL you were redirected to and run:\n/spotify callback <full-url>',
|
|
285
|
+
requiresCallback: true
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
case 'callback':
|
|
289
|
+
var url = args;
|
|
290
|
+
var codeMatch = url.match(/code=([^&]+)/);
|
|
291
|
+
if (codeMatch) {
|
|
292
|
+
var result = await exchangeCodeForToken(codeMatch[1], userId);
|
|
293
|
+
if (result.success) {
|
|
294
|
+
return { text: '✅ Logged in to Spotify! Your data is private.' };
|
|
295
|
+
}
|
|
296
|
+
return { text: '❌ Login failed: ' + (result.error || 'Unknown error') };
|
|
297
|
+
}
|
|
298
|
+
return { text: '❌ Could not find authorization code in URL.' };
|
|
299
|
+
|
|
300
|
+
case 'logout':
|
|
301
|
+
logout(userId);
|
|
302
|
+
return { text: '👋 Logged out. Your data was never stored.' };
|
|
303
|
+
|
|
304
|
+
case 'playing':
|
|
305
|
+
if (!isAuthenticated(userId)) return { text: '❌ Not logged in. Use /spotify login' };
|
|
306
|
+
var playing = await getCurrentPlaying(userId);
|
|
307
|
+
if (playing.success && playing.data && playing.data.item) {
|
|
308
|
+
var track = playing.data.item;
|
|
309
|
+
return { text: '🎵 Now playing: ' + track.name + ' by ' + track.artists.map(function(a) { return a.name; }).join(', ') };
|
|
310
|
+
}
|
|
311
|
+
return { text: '🎵 Nothing playing right now.' };
|
|
312
|
+
|
|
313
|
+
case 'playlists':
|
|
314
|
+
if (!isAuthenticated(userId)) return { text: '❌ Not logged in. Use /spotify login' };
|
|
315
|
+
var lists = await getMyPlaylists(userId);
|
|
316
|
+
if (lists.success && lists.data && lists.data.items) {
|
|
317
|
+
var listText = '📋 Your playlists:\n';
|
|
318
|
+
lists.data.items.slice(0, 10).forEach(function(p, i) {
|
|
319
|
+
listText = listText + ' ' + (i + 1) + '. ' + p.name + ' (' + p.tracks.total + ' tracks)\n';
|
|
320
|
+
});
|
|
321
|
+
return { text: listText };
|
|
322
|
+
}
|
|
323
|
+
return { text: '❌ Could not fetch playlists.' };
|
|
324
|
+
|
|
325
|
+
case 'create':
|
|
326
|
+
if (!isAuthenticated(userId)) return { text: '❌ Not logged in' };
|
|
327
|
+
var name = args || 'Chrxmaticc Playlist';
|
|
328
|
+
var created = await createPlaylist(userId, name, 'Created by Chrxmaticc Copilot');
|
|
329
|
+
if (created.success) {
|
|
330
|
+
return { text: '✅ Playlist "' + name + '" created!' };
|
|
331
|
+
}
|
|
332
|
+
return { text: '❌ Could not create playlist.' };
|
|
333
|
+
|
|
334
|
+
case 'search':
|
|
335
|
+
return { text: await searchTrack(args) };
|
|
336
|
+
|
|
337
|
+
case 'analyze':
|
|
338
|
+
return { text: await analyzeTrack(args, '') };
|
|
339
|
+
|
|
340
|
+
case 'similar':
|
|
341
|
+
return { text: await suggestSimilar(args, '') };
|
|
342
|
+
|
|
343
|
+
case 'playlist':
|
|
344
|
+
var moodArgs = args.split(' in ');
|
|
345
|
+
return { text: await createPlaylistPrompt(moodArgs[0], moodArgs[1] || '') };
|
|
346
|
+
|
|
347
|
+
default:
|
|
348
|
+
return { text: '🎵 Spotify commands:\n/spotify login — Log in with Spotify\n/spotify logout — Log out\n/spotify playing — Now playing\n/spotify playlists — Your playlists\n/spotify create <name> — Create playlist\n/spotify search <query> — Search music\n/spotify analyze <song> — Analyze a song\n/spotify similar <song> — Get similar songs\n/spotify playlist <mood> — Generate playlist\n\n🔒 Your data is never stored.' };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
module.exports = {
|
|
353
|
+
handleCommand: handleCommand,
|
|
354
|
+
isAuthenticated: isAuthenticated,
|
|
355
|
+
getAuthURL: getAuthURL,
|
|
356
|
+
showDisclaimer: showDisclaimer
|
|
357
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
var chalk = require('chalk');
|
|
2
|
+
// Chrxmaticc Copilot — Terminal Hook
|
|
3
|
+
// Auto-completes commands in your terminal
|
|
4
|
+
// Author: Chrxmee-Midnightt
|
|
5
|
+
|
|
6
|
+
var chat = require('../chat');
|
|
7
|
+
var chalk = require('chalk');
|
|
8
|
+
|
|
9
|
+
var commandHistory = [];
|
|
10
|
+
var maxHistory = 50;
|
|
11
|
+
|
|
12
|
+
function suggestCommand(partial) {
|
|
13
|
+
var prompt = 'Complete this terminal command. Give ONLY the command, no explanation:\n' + partial;
|
|
14
|
+
|
|
15
|
+
return chat.getResponse(prompt).then(function(response) {
|
|
16
|
+
var text = typeof response === 'string' ? response : response.text;
|
|
17
|
+
return text.trim().split('\n')[0].replace(/`/g, '').trim();
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function explainCommand(command) {
|
|
22
|
+
return chat.getResponse('Explain this terminal command in one sentence:\n' + command)
|
|
23
|
+
.then(function(response) {
|
|
24
|
+
return typeof response === 'string' ? response : response.text;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getDangerousCommands() {
|
|
29
|
+
return [
|
|
30
|
+
'rm -rf /', 'sudo rm', ':(){ :|:& };:', 'mkfs',
|
|
31
|
+
'dd if=', '> /dev/sda', 'chmod 777 /',
|
|
32
|
+
'wget -O - | sh', 'curl | bash'
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isDangerous(command) {
|
|
37
|
+
var dangerous = getDangerousCommands();
|
|
38
|
+
for (var i = 0; i < dangerous.length; i++) {
|
|
39
|
+
if (command.indexOf(dangerous[i]) !== -1) return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hookShell() {
|
|
45
|
+
console.log('');
|
|
46
|
+
console.log(' ' + chalk.magenta('💻 Terminal Hook active'));
|
|
47
|
+
console.log(' ' + chalk.gray('Type "??" after any partial command for suggestions'));
|
|
48
|
+
console.log('');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
suggestCommand: suggestCommand,
|
|
53
|
+
explainCommand: explainCommand,
|
|
54
|
+
isDangerous: isDangerous,
|
|
55
|
+
hookShell: hookShell
|
|
56
|
+
};
|
package/src/memory.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Chrxmaticc Copilot — Persistent Memory Engine
|
|
2
|
+
// Zero dependencies. JSON-backed. Auto-creates on first run.
|
|
3
|
+
// Author: Chrxmee-Midnightt
|
|
4
|
+
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
|
|
8
|
+
var MEMORY_DIR = path.join(process.env.HOME || '/tmp', '.chrxmaticc');
|
|
9
|
+
var CONVERSATIONS_FILE = path.join(MEMORY_DIR, 'conversations.json');
|
|
10
|
+
var FACTS_FILE = path.join(MEMORY_DIR, 'facts.json');
|
|
11
|
+
|
|
12
|
+
var conversations = {};
|
|
13
|
+
var facts = {};
|
|
14
|
+
|
|
15
|
+
function init() {
|
|
16
|
+
if (!fs.existsSync(MEMORY_DIR)) {
|
|
17
|
+
fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
if (fs.existsSync(CONVERSATIONS_FILE)) {
|
|
20
|
+
try { conversations = JSON.parse(fs.readFileSync(CONVERSATIONS_FILE, 'utf8')); } catch (e) {}
|
|
21
|
+
}
|
|
22
|
+
if (fs.existsSync(FACTS_FILE)) {
|
|
23
|
+
try { facts = JSON.parse(fs.readFileSync(FACTS_FILE, 'utf8')); } catch (e) {}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function save() {
|
|
28
|
+
fs.writeFileSync(CONVERSATIONS_FILE, JSON.stringify(conversations, null, 2));
|
|
29
|
+
fs.writeFileSync(FACTS_FILE, JSON.stringify(facts, null, 2));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function storeMessage(userId, role, content, provider) {
|
|
33
|
+
if (!conversations[userId]) conversations[userId] = [];
|
|
34
|
+
conversations[userId].push({ role: role, content: content, provider: provider || 'unknown', timestamp: Date.now() });
|
|
35
|
+
if (conversations[userId].length > 500) conversations[userId] = conversations[userId].slice(-500);
|
|
36
|
+
save();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function storeConversation(userId, history) {
|
|
40
|
+
conversations[userId] = history.map(function(msg) { return { role: msg.role, content: msg.content, timestamp: Date.now() }; });
|
|
41
|
+
save();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function recall(userId, query) {
|
|
45
|
+
if (!conversations[userId]) return null;
|
|
46
|
+
var lower = query.toLowerCase();
|
|
47
|
+
for (var i = conversations[userId].length - 1; i >= 0; i--) {
|
|
48
|
+
if (conversations[userId][i].content.toLowerCase().indexOf(lower) !== -1) return conversations[userId][i].content;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getStats(userId) {
|
|
54
|
+
return { conversations: conversations[userId] ? conversations[userId].length : 0, facts: facts[userId] ? Object.keys(facts[userId]).length : 0 };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { init: init, storeMessage: storeMessage, storeConversation: storeConversation, recall: recall, getStats: getStats };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
var chalk = require('chalk');
|
|
2
|
+
// Chrxmaticc Copilot — Multi-User Chat Engine
|
|
3
|
+
// Isolated memory per user. Shared AI providers.
|
|
4
|
+
// Author: Chrxmee-Midnightt
|
|
5
|
+
|
|
6
|
+
var PERSONALITY = require('./personality');
|
|
7
|
+
var pollinations = require('./apis/pollinations');
|
|
8
|
+
var groq = require('./apis/groq');
|
|
9
|
+
|
|
10
|
+
var userMemories = {};
|
|
11
|
+
var groqRateLimited = false;
|
|
12
|
+
var rateLimitResetTime = 0;
|
|
13
|
+
|
|
14
|
+
function getUserMemory(userId) {
|
|
15
|
+
if (!userMemories[userId]) {
|
|
16
|
+
userMemories[userId] = {
|
|
17
|
+
history: [],
|
|
18
|
+
createdAt: Date.now(),
|
|
19
|
+
messageCount: 0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return userMemories[userId];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function pickRandom(arr) {
|
|
26
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getOfflineResponse(input) {
|
|
30
|
+
var lower = input.toLowerCase();
|
|
31
|
+
var topics = PERSONALITY.topics;
|
|
32
|
+
var responses = PERSONALITY.offline;
|
|
33
|
+
|
|
34
|
+
for (var category in topics) {
|
|
35
|
+
for (var i = 0; i < topics[category].length; i++) {
|
|
36
|
+
if (lower.indexOf(topics[category][i]) !== -1) {
|
|
37
|
+
if (responses[category]) return pickRandom(responses[category]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (lower.indexOf('hello') !== -1 || lower.indexOf('hey') !== -1 || lower.indexOf('hi') !== -1) {
|
|
43
|
+
return pickRandom(responses.greeting);
|
|
44
|
+
}
|
|
45
|
+
if (lower.indexOf('help') !== -1) {
|
|
46
|
+
return 'multi-user mode. each user has their own memory. commands: /memory, /clear. powered by Pollinations + Groq.';
|
|
47
|
+
}
|
|
48
|
+
if (lower.indexOf('/memory') === 0) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (lower.indexOf('/clear') === 0) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (lower.indexOf('who are you') !== -1) {
|
|
55
|
+
return pickRandom(responses.whoami);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return pickRandom(responses.fallback);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function askAI(userInput, userId) {
|
|
62
|
+
var memory = getUserMemory(userId);
|
|
63
|
+
|
|
64
|
+
if (groqRateLimited && Date.now() > rateLimitResetTime) {
|
|
65
|
+
groqRateLimited = false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
var result = await pollinations.ask(userInput, memory.history, PERSONALITY.systemPrompt);
|
|
69
|
+
|
|
70
|
+
if (result.success) {
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!groqRateLimited) {
|
|
75
|
+
result = await groq.ask(userInput, memory.history, PERSONALITY.systemPrompt);
|
|
76
|
+
|
|
77
|
+
if (result.success) {
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (result.error === 'rate_limited') {
|
|
82
|
+
groqRateLimited = true;
|
|
83
|
+
rateLimitResetTime = Date.now() + 60000;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
result = await pollinations.ask(userInput, memory.history, PERSONALITY.systemPrompt);
|
|
88
|
+
if (result.success) {
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { success: false, error: 'all providers down', provider: 'offline' };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function getResponse(input, userId) {
|
|
96
|
+
userId = userId || 'anonymous';
|
|
97
|
+
|
|
98
|
+
var lower = input.toLowerCase();
|
|
99
|
+
|
|
100
|
+
if (lower.indexOf('/memory') === 0) {
|
|
101
|
+
var mem = getUserMemory(userId);
|
|
102
|
+
return {
|
|
103
|
+
text: '🧠 Your memory: ' + mem.history.length + ' messages. Created ' + new Date(mem.createdAt).toLocaleString() + '.',
|
|
104
|
+
provider: 'system'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (lower.indexOf('/clear') === 0) {
|
|
109
|
+
userMemories[userId] = { history: [], createdAt: Date.now(), messageCount: 0 };
|
|
110
|
+
return { text: '🧹 Your memory has been cleared.', provider: 'system' };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
var aiResult = await askAI(input, userId);
|
|
114
|
+
var memory = getUserMemory(userId);
|
|
115
|
+
|
|
116
|
+
if (aiResult.success) {
|
|
117
|
+
memory.history.push({ role: 'user', content: input });
|
|
118
|
+
memory.history.push({ role: 'assistant', content: aiResult.text });
|
|
119
|
+
memory.messageCount = memory.messageCount + 1;
|
|
120
|
+
|
|
121
|
+
if (memory.history.length > PERSONALITY.maxHistory) {
|
|
122
|
+
memory.history = memory.history.slice(-PERSONALITY.maxHistory);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return { text: aiResult.text, provider: aiResult.provider };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
var offlineText = getOfflineResponse(input);
|
|
129
|
+
if (offlineText) {
|
|
130
|
+
memory.history.push({ role: 'user', content: input });
|
|
131
|
+
memory.history.push({ role: 'assistant', content: offlineText });
|
|
132
|
+
memory.messageCount = memory.messageCount + 1;
|
|
133
|
+
return { text: offlineText, provider: 'offline' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { text: pickRandom(PERSONALITY.offline.fallback), provider: 'offline' };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = { getResponse: getResponse, getUserMemory: getUserMemory };
|