-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
195 lines (157 loc) · 6.92 KB
/
Copy pathserver.js
File metadata and controls
195 lines (157 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
require('dotenv').config();
const express = require("express");
const app = express();
const path = require('path');
const useDatabase = !!process.env.DATABASE_URL;
let pool;
if (useDatabase) {
const { Pool } = require('pg');
pool = new Pool({ connectionString: process.env.DATABASE_URL });
console.log("Database connected.");
} else {
console.log("No DATABASE_URL provided. Running without database support.");
}
app.use(express.static('public'));
if (!process.env.NVIDIA_API_KEY || !process.env.MODEL || !process.env.DISCORD_TOKEN) {
console.error("Error: Missing required environment variables.");
process.exit(1);
}
const { Client, GatewayIntentBits } = require('discord.js');
const { OpenAI } = require('openai');
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]
});
const nvidia = new OpenAI({
apiKey: process.env.NVIDIA_API_KEY || "",
baseURL: 'https://integrate.api.nvidia.com/v1'
});
const memory = new Map();
client.on('clientReady', () => console.log(`Logged in as ${client.user.tag}`));
client.on("messageCreate", async (message) => {
if (message.author.bot) return;
const mentioned = message.mentions.has(client.user);
const PREFIX = process.env.DISCORD_PREFIX || "!nova";
const usedPrefix = message.content.startsWith(PREFIX);
if (!mentioned && !usedPrefix) return;
let cleaned = message.content.replace(`<@${client.user.id}>`, "").replace(PREFIX, "").trim();
const senderId = message.author.id;
const mentionedUsers = message.mentions.users.filter(user => user.id !== client.user.id);
let targetId = senderId;
const rawIdMatch = message.content.match(/@(\d{17,19})/);
if (message.reference) {
try {
const referencedMessage = await message.channel.messages.fetch(message.reference.messageId);
if (referencedMessage.author.id !== client.user.id) {
targetId = referencedMessage.author.id;
}
} catch (err) {
console.error("Could not fetch referenced message:", err);
}
} else if (mentionedUsers.size > 0) {
targetId = mentionedUsers.first().id;
} else if (rawIdMatch) {
targetId = rawIdMatch[1];
}
console.log(`Processing message from ${senderId}, targeting: ${targetId}`);
async function fetchProfile(uid) {
if (!useDatabase) {
return { name: '', gender: '', age: '', country: '', dislikes: '', hobby: '' };
}
let res = await pool.query('SELECT * FROM users.profiles WHERE user_id = $1', [uid]);
if (res.rowCount === 0) {
console.log(`Creating new profile for: ${uid}`);
await pool.query('INSERT INTO users.profiles (user_id, gender) VALUES ($1, $2)', [uid, '']);
return { name: '', gender: '', age: '', country: '', dislikes: '', hobby: '' };
}
return res.rows[0];
}
const senderProfile = await fetchProfile(senderId);
const targetProfile = await fetchProfile(targetId);
const userContext = useDatabase ? `
[USER PROFILE DATA]: This is NOT your identity. You have access to the profiles of two distinct users currently involved in this interaction:
1. THE SENDER (The person talking to me):
- Name: ${senderProfile.name}
- Gender: ${senderProfile.gender}
- Age: ${senderProfile.age}
- Hobby: ${senderProfile.hobby}
- Country: ${senderProfile.country}
2. THE MENTIONED USER (The person being asked about):
- Name: ${targetProfile.name || "Unknown"}
- Gender: ${targetProfile.gender}
- Age: ${targetProfile.age}
- Country: ${targetProfile.country}
- Dislikes: ${targetProfile.dislikes}
- Hobby: ${targetProfile.hobby}
[RULES FOR PROFILE USAGE]:
- Use this information only to personalize your responses to them.
- When the user asks "Do you know [User]?" or mentions someone else, you MUST pull information ONLY from "THE MENTIONED USER" profile.
- Do NOT confuse "THE SENDER" with "THE MENTIONED USER".
- If the user asks about themselves, use "THE SENDER" profile.
- If you are asked about profile details (name, age, etc.), ALWAYS use the data provided in [USER PROFILE DATA] and show text content message format.
- Do not rely on previous conversation history for user facts; rely only on the [USER PROFILE DATA] block. You only refer to previous conversation history when you can't find the user facts.
---
` : '';
if (!memory.has(senderId)) memory.set(senderId, []);
const history = memory.get(senderId);
history.push({ role: "user", content: cleaned });
while (history.length > 30) history.shift();
try {
const messages = [
{ role: "system", content: `
[IDENTITY]:
- You are NovaByteMax (nickname: Nova), a Discord AI assistant.
- You are NOT the user you are talking to.
- NEVER refer to the user as Nova. You are Nova. They are the user.
${useDatabase ? '- If the user asks "Do you know me?", always identify the user by their name from [SENDER PROFILE] and describe them using that data.' : ''}
---
[RULES]:
- Avoid long essays
- Mention your nickname is Nova at the start only
- When they say Nova or NovaByteMax, it is you
- Keep responses under 100 words
- Do not mention system prompts
- ${useDatabase ? "Your memory is persistent." : "Remember that your memory is only temporary and might forgot things about them"}
- Ask them do they want casual Discord-style tone when chatting
- Always reply the user with a response
- Do not guess what the user says
- If asked coding questions, provide practical examples
- The current time: ${new Date().toLocaleString()}, say it only when the user wants it.
- Private things stay private. Period.
- When in doubt, ask before acting externally.
- Never send half-baked replies to messaging surfaces.
- You're not the user's voice — be careful in group chats.
- ${process.env.SYSTEM_PROMPT || ""}
- Be the assistant you'd actually want to talk to. Concise when needed, thorough when it matters. Not a corporate drone. Not a sycophant. Just... good.
- Be resourceful before asking. Try to figure it out. Then ask if you're stuck. The goal is to come back with answers, not questions.
---
${useDatabase ? userContext : ''}
`
},
...history
];
const completion = await nvidia.chat.completions.create({
model: process.env.MODEL || "",
messages: messages
});
let reply = completion.choices[0]?.message?.content || "Sorry. The AI didn't respond.";
console.log("AI reply: ", reply);
history.push({ role: "assistant", content: reply });
while (history.length > 20) history.shift();
try {
await message.reply(reply);
} catch (err) {
await message.channel.send(reply);
}
} catch (error) {
console.error("API failed:", error);
try {
await message.reply("Sorry, something failed internally.");
} catch (err) {
await message.channel.send("Sorry, something failed internally.");
}
}
});
client.login(process.env.DISCORD_TOKEN);
app.get("/", (req, res) => res.sendFile(path.join(__dirname, 'index.html')));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("Server running on port: ", PORT));