feat(bots): Implement comprehensive bot system with autonomous posting, content management, and API endpoints

- Add bot management system with creation, suspension, and reinstatement functionality
- Implement autonomous bot posting with scheduling, rate limiting, and content generation
- Add content fetching system supporting RSS feeds and multiple content sources
- Implement LLM-based content generation with customizable bot personalities
- Add mention handling and automated response system for bot interactions
- Implement API key management with encryption using AUTH_SECRET for simplified deployment
- Add comprehensive bot logging system for activity tracking and error monitoring
- Create bot administration pages and settings UI for managing bot configurations
- Add database migrations for bot system schema including users, sources, and content items
- Implement cron job system for automated bot operations and scheduled tasks
- Add extensive test coverage with unit and property-based tests for core bot modules
- Simplify encryption by deriving keys from AUTH_SECRET instead of separate environment variable
- Implement automatic content fetching on post trigger with retry logic
- Add Reddit-specific link preview handling using oEmbed API for reliable metadata extraction
- Create utility scripts for bot inspection and cleanup operations
- Add comprehensive bot system documentation and improvement tracking
This commit is contained in:
AskIt
2026-01-25 16:22:41 +01:00
parent cfb558fff1
commit 8ad3b97b7e
102 changed files with 41692 additions and 164 deletions
+15 -12
View File
@@ -31,18 +31,21 @@ export async function GET(request: Request) {
limit,
});
const sanitized = results.map((post) => ({
id: post.id,
content: post.content,
createdAt: post.createdAt,
isRemoved: post.isRemoved,
removedReason: post.removedReason,
author: {
id: post.author.id,
handle: post.author.handle,
displayName: post.author.displayName,
},
}));
const sanitized = results.map((post) => {
const author = post.author as { id: string; handle: string; displayName: string | null };
return {
id: post.id,
content: post.content,
createdAt: post.createdAt,
isRemoved: post.isRemoved,
removedReason: post.removedReason,
author: {
id: author.id,
handle: author.handle,
displayName: author.displayName,
},
};
});
return NextResponse.json({ posts: sanitized });
} catch (error) {
+38 -29
View File
@@ -44,17 +44,20 @@ export async function GET(request: Request) {
})
: [];
const postTargets = postTargetsRaw.map((post) => ({
id: post.id,
content: post.content,
createdAt: post.createdAt,
isRemoved: post.isRemoved,
author: {
id: post.author.id,
handle: post.author.handle,
displayName: post.author.displayName,
},
}));
const postTargets = postTargetsRaw.map((post) => {
const author = post.author as { id: string; handle: string; displayName: string | null };
return {
id: post.id,
content: post.content,
createdAt: post.createdAt,
isRemoved: post.isRemoved,
author: {
id: author.id,
handle: author.handle,
displayName: author.displayName,
},
};
});
const userTargets = userTargetsRaw.map((user) => ({
id: user.id,
@@ -67,24 +70,30 @@ export async function GET(request: Request) {
const postMap = new Map(postTargets.map((post) => [post.id, post]));
const userMap = new Map(userTargets.map((user) => [user.id, user]));
const reportsWithTargets = reportRows.map((report) => ({
id: report.id,
targetType: report.targetType,
targetId: report.targetId,
reason: report.reason,
status: report.status,
createdAt: report.createdAt,
reporter: report.reporter
? { id: report.reporter.id, handle: report.reporter.handle }
: null,
resolver: report.resolver
? { id: report.resolver.id, handle: report.resolver.handle }
: null,
target:
report.targetType === 'post'
? postMap.get(report.targetId) || null
: userMap.get(report.targetId) || null,
}));
type UserInfo = { id: string; handle: string };
const reportsWithTargets = reportRows.map((report) => {
const reporter = report.reporter as UserInfo | null;
const resolver = report.resolver as UserInfo | null;
return {
id: report.id,
targetType: report.targetType,
targetId: report.targetId,
reason: report.reason,
status: report.status,
createdAt: report.createdAt,
reporter: reporter
? { id: reporter.id, handle: reporter.handle }
: null,
resolver: resolver
? { id: resolver.id, handle: resolver.handle }
: null,
target:
report.targetType === 'post'
? postMap.get(report.targetId) || null
: userMap.get(report.targetId) || null,
};
});
return NextResponse.json({ reports: reportsWithTargets });
} catch (error) {
+1
View File
@@ -24,6 +24,7 @@ export async function GET(request: Request) {
isSilenced: users.isSilenced,
silenceReason: users.silenceReason,
createdAt: users.createdAt,
isBot: users.isBot,
})
.from(users)
.orderBy(desc(users.createdAt))