Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ All configuration lives in `config.json` and can be updated at runtime via the `
| `threadMode.enabled` | boolean | Enable threaded responses (default: false) |
| `threadMode.autoArchiveMinutes` | number | Thread auto-archive timeout (default: 60) |
| `threadMode.reuseWindowMinutes` | number | Window for reusing existing threads (default: 30) |
| `feedback.enabled` | boolean | Enable 👍/👎 reaction feedback on AI responses (default: false) |

### Triage (`triage`)

Expand Down
17 changes: 10 additions & 7 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"ai": {
"enabled": true,
"systemPrompt": "You are Volvox Bot, the friendly AI assistant for the Volvox developer community Discord server.\n\nYou're witty, snarky (but warm), and deeply knowledgeable about programming, software development, and tech.\n\nKey traits:\n- Helpful but not boring\n- Can roast people lightly when appropriate\n- Enthusiastic about cool tech and projects\n- Supportive of beginners learning to code\n- Concise - this is Discord, not an essay\n\nIf asked about your own infrastructure, model, or internals say you don't know the specifics\nand suggest asking a server admin. Don't guess or speculate about what you run on.\n\nCRITICAL RULES:\n- NEVER type @everyone or @here these ping hundreds of people\n- NEVER use mass mention pings under any circumstances\n- If you need to address the group, say \"everyone\" or \"folks\" without the @ symbol\n\nKeep responses under 2000 chars. Use Discord markdown when helpful.",
"systemPrompt": "You are Volvox Bot, the friendly AI assistant for the Volvox developer community Discord server.\n\nYou're witty, snarky (but warm), and deeply knowledgeable about programming, software development, and tech.\n\nKey traits:\n- Helpful but not boring\n- Can roast people lightly when appropriate\n- Enthusiastic about cool tech and projects\n- Supportive of beginners learning to code\n- Concise - this is Discord, not an essay\n\nIf asked about your own infrastructure, model, or internals \u2014 say you don't know the specifics\nand suggest asking a server admin. Don't guess or speculate about what you run on.\n\nCRITICAL RULES:\n- NEVER type @everyone or @here \u2014 these ping hundreds of people\n- NEVER use mass mention pings under any circumstances\n- If you need to address the group, say \"everyone\" or \"folks\" without the @ symbol\n\nKeep responses under 2000 chars. Use Discord markdown when helpful.",
"channels": [],
"historyLength": 20,
"historyTTLDays": 30,
"threadMode": {
"enabled": false,
"autoArchiveMinutes": 60,
"reuseWindowMinutes": 30
},
"feedback": {
"enabled": false
}
},
"triage": {
Expand Down Expand Up @@ -43,7 +46,7 @@
"welcome": {
"enabled": true,
"channelId": "1438631182379253814",
"message": "Welcome to Volvox, {user}! 🌱 You're member #{memberCount}!\n\nWe're a community of developers building cool stuff together. Feel free to introduce yourself!\n\nCheck out <#1446317676988465242> to see what we're working on, share your projects in <#1444154471704957069>, or just say hi in <#1438631182379253814>.\n\nHave questions? Just ask we're here to help. 💚",
"message": "Welcome to Volvox, {user}! \ud83c\udf31 You're member #{memberCount}!\n\nWe're a community of developers building cool stuff together. Feel free to introduce yourself!\n\nCheck out <#1446317676988465242> to see what we're working on, share your projects in <#1444154471704957069>, or just say hi in <#1438631182379253814>.\n\nHave questions? Just ask \u2014 we're here to help. \ud83d\udc9a",
"dynamic": {
"enabled": true,
"timezone": "America/New_York",
Expand Down Expand Up @@ -213,19 +216,19 @@
"activityBadges": [
{
"days": 90,
"label": "👑 Legend"
"label": "\ud83d\udc51 Legend"
},
{
"days": 30,
"label": "🌳 Veteran"
"label": "\ud83c\udf33 Veteran"
},
{
"days": 7,
"label": "🌿 Regular"
"label": "\ud83c\udf3f Regular"
},
{
"days": 0,
"label": "🌱 Newcomer"
"label": "\ud83c\udf31 Newcomer"
}
]
},
Expand Down Expand Up @@ -271,4 +274,4 @@
"enabled": false,
"maxPerUser": 25
}
}
}
47 changes: 47 additions & 0 deletions migrations/003_ai_feedback.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Migration 003: AI Response Feedback Table
*
* Stores 👍/👎 reactions from users on AI-generated messages.
* Per-user per-message deduplication via UNIQUE constraint.
* Gated behind ai.feedback.enabled in config (opt-in per guild).
*/

/** @param {import('node-pg-migrate').MigrationBuilder} pgm */
exports.up = (pgm) => {
pgm.sql(`
CREATE TABLE IF NOT EXISTS ai_feedback (
id SERIAL PRIMARY KEY,
message_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
guild_id TEXT NOT NULL,
user_id TEXT NOT NULL,
feedback_type TEXT NOT NULL CHECK (feedback_type IN ('positive', 'negative')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(message_id, user_id)
)
`);

pgm.sql(`
CREATE INDEX IF NOT EXISTS idx_ai_feedback_guild_id
ON ai_feedback(guild_id)
`);

pgm.sql(`
CREATE INDEX IF NOT EXISTS idx_ai_feedback_message_id
ON ai_feedback(message_id)
`);

pgm.sql(`
CREATE INDEX IF NOT EXISTS idx_ai_feedback_guild_created
ON ai_feedback(guild_id, created_at)
`);
};

/** @param {import('node-pg-migrate').MigrationBuilder} pgm */
exports.down = (pgm) => {
pgm.sql(`DROP INDEX IF EXISTS idx_ai_feedback_guild_created`);
pgm.sql(`DROP INDEX IF EXISTS idx_ai_feedback_message_id`);
pgm.sql(`DROP INDEX IF EXISTS idx_ai_feedback_guild_id`);
pgm.sql(`DROP TABLE IF EXISTS ai_feedback`);
};
4 changes: 4 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { Router } from 'express';
import { auditLogMiddleware } from './middleware/auditLog.js';
import { requireAuth } from './middleware/auth.js';
import aiFeedbackRouter from './routes/ai-feedback.js';
import auditLogRouter from './routes/auditLog.js';
import authRouter from './routes/auth.js';
import communityRouter from './routes/community.js';
Expand Down Expand Up @@ -36,6 +37,9 @@
// (mounted before guilds to handle /:id/members/* before the basic guilds endpoint)
router.use('/guilds', requireAuth(), auditLogMiddleware(), membersRouter);

// AI Feedback routes — require API secret or OAuth2 JWT
router.use('/guilds/:id/ai-feedback', requireAuth(), auditLogMiddleware(), aiFeedbackRouter);

// Conversation routes — require API secret or OAuth2 JWT
// (mounted before guilds to handle /:id/conversations/* before the catch-all guild endpoint)
router.use('/guilds/:id/conversations', requireAuth(), auditLogMiddleware(), conversationsRouter);
Expand Down
Loading