Replace PostgreSQL and Docker deployment with embedded Turso, Drizzle relational queries v2, native systemd deployment on port 43821, and fresh SQLite migrations.

Hop-State: A_06FP5KEDBTB4A9ZT7QB498G
Hop-Proposal: R_06FP5KDWMCKVVMQZT4MVZ28
Hop-Task: T_06FP5DZ7T0G45FG93PT90B8
Hop-Attempt: AT_06FP5DZ7T0PKQW99V27JCV8
This commit is contained in:
2026-07-14 15:44:42 -07:00
committed by Hop
184 changed files with 8375 additions and 53944 deletions
+13 -23
View File
@@ -95,26 +95,19 @@ export async function getLogsForBot(
botId: string,
options: LogQueryOptions = {}
): Promise<ActivityLog[]> {
const conditions = [eq(botActivityLogs.botId, botId)];
// Filter by action types
if (options.actionTypes && options.actionTypes.length > 0) {
conditions.push(inArray(botActivityLogs.action, options.actionTypes));
}
// Filter by date range
if (options.startDate) {
conditions.push(gte(botActivityLogs.createdAt, options.startDate));
}
if (options.endDate) {
conditions.push(lte(botActivityLogs.createdAt, options.endDate));
}
// Build query
let query = db.query.botActivityLogs.findMany({
where: and(...conditions),
orderBy: [desc(botActivityLogs.createdAt)], // Reverse chronological
where: {
botId,
...(options.actionTypes?.length ? { action: { in: options.actionTypes } } : {}),
...(options.startDate || options.endDate ? {
createdAt: {
...(options.startDate ? { gte: options.startDate } : {}),
...(options.endDate ? { lte: options.endDate } : {}),
},
} : {}),
},
orderBy: () => [desc(botActivityLogs.createdAt)], // Reverse chronological
limit: options.limit || 100,
offset: options.offset || 0,
});
@@ -146,11 +139,8 @@ export async function getErrorLogs(
limit: number = 50
): Promise<ActivityLog[]> {
const logs = await db.query.botActivityLogs.findMany({
where: and(
eq(botActivityLogs.botId, botId),
eq(botActivityLogs.success, false)
),
orderBy: [desc(botActivityLogs.createdAt)],
where: { AND: [{ botId: botId }, { success: false }] },
orderBy: () => [desc(botActivityLogs.createdAt)],
limit,
});
+11 -17
View File
@@ -114,10 +114,10 @@ export const MIN_INTEREST_SCORE = 60;
*/
async function hasContentSources(botId: string): Promise<boolean> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
contentSources: {
where: (sources, { eq }) => eq(sources.isActive, true),
where: { isActive: true },
},
},
});
@@ -139,10 +139,10 @@ async function getUnprocessedContentItems(
): Promise<ContentItem[]> {
// Get bot's content sources
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
contentSources: {
where: (sources, { eq }) => eq(sources.isActive, true),
where: { isActive: true },
},
},
});
@@ -155,9 +155,7 @@ async function getUnprocessedContentItems(
// Get unprocessed content items from these sources
const items = await db.query.botContentItems.findMany({
where: and(
eq(botContentItems.isProcessed, false)
),
where: { AND: [{ isProcessed: false }] },
orderBy: (items, { desc }) => [desc(items.publishedAt)],
limit,
});
@@ -284,7 +282,7 @@ export async function evaluateContentForPosting(
): Promise<AutonomousPostEvaluation> {
// Get bot with user relation
const dbBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: { user: true },
});
@@ -373,7 +371,7 @@ export async function attemptAutonomousPost(
// Get bot with schedule config
const dbBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: { user: true },
});
@@ -541,11 +539,7 @@ export async function processAllAutonomousBots(): Promise<Array<{
}>> {
// Get all active bots with autonomous mode enabled
const autonomousBots = await db.query.bots.findMany({
where: and(
eq(bots.isActive, true),
eq(bots.isSuspended, false),
eq(bots.autonomousMode, true)
),
where: { AND: [{ isActive: true }, { isSuspended: false }, { autonomousMode: true }] },
with: { user: true },
});
@@ -590,7 +584,7 @@ export async function canPostAutonomously(botId: string): Promise<{
}> {
// Get bot
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
@@ -686,7 +680,7 @@ export async function getAutonomousPostingStats(botId: string): Promise<{
}> {
// Get bot's content sources
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
contentSources: true,
},
@@ -706,7 +700,7 @@ export async function getAutonomousPostingStats(botId: string): Promise<{
// Get all content items for this bot's sources
const allItems = await db.query.botContentItems.findMany({
where: (items, { inArray }) => inArray(items.sourceId, sourceIds),
where: { sourceId: { in: sourceIds } },
});
const processedItems = allItems.filter(item => item.isProcessed);
+18 -21
View File
@@ -396,7 +396,7 @@ export async function createBot(ownerId: string, config: BotCreateInput): Promis
// Check if handle is taken (in users table now)
const existingUser = await db.query.users.findFirst({
where: eq(users.handle, config.handle.toLowerCase()),
where: { handle: config.handle.toLowerCase() },
});
if (existingUser) {
@@ -405,7 +405,7 @@ export async function createBot(ownerId: string, config: BotCreateInput): Promis
// Get owner to access their S3 storage for bot avatar
const owner = await db.query.users.findFirst({
where: eq(users.id, ownerId),
where: { id: ownerId },
});
if (!owner) {
@@ -420,7 +420,7 @@ export async function createBot(ownerId: string, config: BotCreateInput): Promis
const encryptedPrivateKey = encryptApiKey(privateKey);
// Generate a DID for the bot
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:43821';
const botDid = `did:web:${nodeDomain}:users:${config.handle.toLowerCase()}`;
// Generate bot avatar using owner's S3 storage if no avatar provided
@@ -515,7 +515,7 @@ export async function updateBot(botId: string, config: BotUpdateInput): Promise<
// Check if bot exists and get its user account
const existingBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!existingBot) {
@@ -524,7 +524,7 @@ export async function updateBot(botId: string, config: BotUpdateInput): Promise<
// Get the bot's user account
const botUser = await db.query.users.findFirst({
where: eq(users.id, existingBot.userId),
where: { id: existingBot.userId },
});
if (!botUser) {
@@ -629,7 +629,7 @@ export async function updateBot(botId: string, config: BotUpdateInput): Promise<
// Get updated user
const updatedUser = await db.query.users.findFirst({
where: eq(users.id, existingBot.userId),
where: { id: existingBot.userId },
});
return dbBotToBot(updatedBot, updatedUser!);
@@ -647,7 +647,7 @@ export async function updateBot(botId: string, config: BotUpdateInput): Promise<
export async function deleteBot(botId: string): Promise<void> {
// Check if bot exists
const existingBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!existingBot) {
@@ -671,14 +671,14 @@ export async function deleteBot(botId: string): Promise<void> {
*/
export async function getBotsByUser(ownerId: string): Promise<Bot[]> {
const userBots = await db.query.bots.findMany({
where: eq(bots.ownerId, ownerId),
where: { ownerId: ownerId },
orderBy: (bots, { desc }) => [desc(bots.createdAt)],
});
// Get all bot user accounts
const botUserIds = userBots.map(b => b.userId);
const botUsers = await db.query.users.findMany({
where: (users, { inArray }) => inArray(users.id, botUserIds),
where: { id: { in: botUserIds } },
});
const userMap = new Map(botUsers.map(u => [u.id, u]));
@@ -702,7 +702,7 @@ export async function getBotsByUser(ownerId: string): Promise<Bot[]> {
*/
export async function getBotById(botId: string): Promise<Bot | null> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
@@ -711,7 +711,7 @@ export async function getBotById(botId: string): Promise<Bot | null> {
// Get the bot's user account
const botUser = await db.query.users.findFirst({
where: eq(users.id, bot.userId),
where: { id: bot.userId },
});
if (!botUser) {
@@ -730,10 +730,7 @@ export async function getBotById(botId: string): Promise<Bot | null> {
export async function getBotByHandle(handle: string): Promise<Bot | null> {
// Find the user with this handle that is a bot
const botUser = await db.query.users.findFirst({
where: and(
eq(users.handle, handle.toLowerCase()),
eq(users.isBot, true)
),
where: { AND: [{ handle: handle.toLowerCase() }, { isBot: true }] },
});
if (!botUser) {
@@ -742,7 +739,7 @@ export async function getBotByHandle(handle: string): Promise<Bot | null> {
// Find the bot config for this user
const bot = await db.query.bots.findFirst({
where: eq(bots.userId, botUser.id),
where: { userId: botUser.id },
});
if (!bot) {
@@ -789,7 +786,7 @@ export async function canUserCreateBot(ownerId: string): Promise<boolean> {
*/
export async function userOwnsBot(ownerId: string, botId: string): Promise<boolean> {
const bot = await db.query.bots.findFirst({
where: and(eq(bots.id, botId), eq(bots.ownerId, ownerId)),
where: { AND: [{ id: botId }, { ownerId: ownerId }] },
});
return bot !== undefined;
@@ -836,7 +833,7 @@ export async function setApiKey(
): Promise<void> {
// Check if bot exists
const existingBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!existingBot) {
@@ -885,7 +882,7 @@ export async function setApiKey(
export async function removeApiKey(botId: string): Promise<void> {
// Check if bot exists
const existingBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!existingBot) {
@@ -930,7 +927,7 @@ export interface ApiKeyStatus {
export async function getApiKeyStatus(botId: string): Promise<ApiKeyStatus> {
// Get the bot with encrypted API key
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
@@ -984,7 +981,7 @@ export async function botHasApiKey(botId: string): Promise<boolean> {
export async function getDecryptedApiKey(botId: string): Promise<string | null> {
// Get the bot with encrypted API key
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
+6 -15
View File
@@ -562,10 +562,7 @@ export async function contentItemExists(
externalId: string
): Promise<boolean> {
const existing = await db.query.botContentItems.findFirst({
where: and(
eq(botContentItems.sourceId, sourceId),
eq(botContentItems.externalId, externalId)
),
where: { AND: [{ sourceId: sourceId }, { externalId: externalId }] },
columns: { id: true },
});
@@ -684,7 +681,7 @@ export async function recordFetchError(
): Promise<void> {
// Get current consecutive errors
const source = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, sourceId),
where: { id: sourceId },
columns: { consecutiveErrors: true },
});
@@ -768,7 +765,7 @@ export async function fetchContent(
case 'news_api':
// Decrypt API key
const dbSource = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, sourceId),
where: { id: sourceId },
columns: { apiKeyEncrypted: true },
});
@@ -785,7 +782,7 @@ export async function fetchContent(
case 'brave_news':
// Decrypt API key
const braveDbSource = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, sourceId),
where: { id: sourceId },
columns: { apiKeyEncrypted: true, sourceConfig: true },
});
@@ -945,10 +942,7 @@ export async function fetchAllSourcesForBot(
options: FetchOptions = {}
): Promise<FetchResult[]> {
const sources = await db.query.botContentSources.findMany({
where: and(
eq(botContentSources.botId, botId),
eq(botContentSources.isActive, true)
),
where: { AND: [{ botId: botId }, { isActive: true }] },
});
const results: FetchResult[] = [];
@@ -994,10 +988,7 @@ export async function getUnprocessedItems(
limit: number = 10
): Promise<StoredContentItem[]> {
const items = await db.query.botContentItems.findMany({
where: and(
eq(botContentItems.sourceId, sourceId),
eq(botContentItems.isProcessed, false)
),
where: { AND: [{ sourceId: sourceId }, { isProcessed: false }] },
orderBy: (items, { asc }) => [asc(items.publishedAt)],
limit,
});
+9 -18
View File
@@ -592,7 +592,7 @@ export async function addSource(
// Check if bot exists
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: { id: true },
});
@@ -646,7 +646,7 @@ export async function addSource(
export async function removeSource(sourceId: string): Promise<void> {
// Check if source exists
const existingSource = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, sourceId),
where: { id: sourceId },
columns: { id: true },
});
@@ -666,7 +666,7 @@ export async function removeSource(sourceId: string): Promise<void> {
*/
export async function getSourceById(sourceId: string): Promise<ContentSource | null> {
const source = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, sourceId),
where: { id: sourceId },
});
if (!source) {
@@ -686,7 +686,7 @@ export async function getSourceById(sourceId: string): Promise<ContentSource | n
*/
export async function getSourcesByBot(botId: string): Promise<ContentSource[]> {
const sources = await db.query.botContentSources.findMany({
where: eq(botContentSources.botId, botId),
where: { botId: botId },
orderBy: (sources, { desc }) => [desc(sources.createdAt)],
});
@@ -701,10 +701,7 @@ export async function getSourcesByBot(botId: string): Promise<ContentSource[]> {
*/
export async function getActiveSourcesByBot(botId: string): Promise<ContentSource[]> {
const sources = await db.query.botContentSources.findMany({
where: and(
eq(botContentSources.botId, botId),
eq(botContentSources.isActive, true)
),
where: { AND: [{ botId: botId }, { isActive: true }] },
orderBy: (sources, { desc }) => [desc(sources.createdAt)],
});
@@ -728,7 +725,7 @@ export async function updateSource(
): Promise<ContentSource> {
// Check if source exists
const existingSource = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, sourceId),
where: { id: sourceId },
});
if (!existingSource) {
@@ -811,10 +808,7 @@ export async function deactivateSource(sourceId: string): Promise<void> {
*/
export async function botOwnsSource(botId: string, sourceId: string): Promise<boolean> {
const source = await db.query.botContentSources.findFirst({
where: and(
eq(botContentSources.id, sourceId),
eq(botContentSources.botId, botId)
),
where: { AND: [{ id: sourceId }, { botId: botId }] },
columns: { id: true },
});
@@ -829,7 +823,7 @@ export async function botOwnsSource(botId: string, sourceId: string): Promise<bo
*/
export async function getSourceCountForBot(botId: string): Promise<number> {
const sources = await db.query.botContentSources.findMany({
where: eq(botContentSources.botId, botId),
where: { botId: botId },
columns: { id: true },
});
@@ -850,10 +844,7 @@ export async function getSourcesByType(
type: ContentSourceType
): Promise<ContentSource[]> {
const sources = await db.query.botContentSources.findMany({
where: and(
eq(botContentSources.botId, botId),
eq(botContentSources.type, type)
),
where: { AND: [{ botId: botId }, { type: type }] },
orderBy: (sources, { desc }) => [desc(sources.createdAt)],
});
+13 -18
View File
@@ -106,7 +106,7 @@ export async function detectMentions(botId: string): Promise<MentionDetectionRes
try {
// Get the bot's handle
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
user: {
columns: { handle: true },
@@ -124,7 +124,7 @@ export async function detectMentions(botId: string): Promise<MentionDetectionRes
// Get existing mention post IDs to avoid duplicates
const existingMentions = await db.query.botMentions.findMany({
where: eq(botMentions.botId, botId),
where: { botId: botId },
columns: { postId: true },
});
@@ -140,9 +140,7 @@ export async function detectMentions(botId: string): Promise<MentionDetectionRes
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
const recentPosts = await db.query.posts.findMany({
where: and(
eq(posts.isRemoved, false)
),
where: { AND: [{ isRemoved: false }] },
with: {
author: {
columns: {
@@ -152,7 +150,7 @@ export async function detectMentions(botId: string): Promise<MentionDetectionRes
},
},
},
orderBy: [desc(posts.createdAt)],
orderBy: () => [desc(posts.createdAt)],
limit: 1000, // Reasonable limit for scanning
});
@@ -218,11 +216,8 @@ export async function detectMentions(botId: string): Promise<MentionDetectionRes
export async function getUnprocessedMentions(botId: string): Promise<Mention[]> {
try {
const mentions = await db.query.botMentions.findMany({
where: and(
eq(botMentions.botId, botId),
eq(botMentions.isProcessed, false)
),
orderBy: [asc(botMentions.createdAt)], // Chronological order (oldest first)
where: { AND: [{ botId: botId }, { isProcessed: false }] },
orderBy: () => [asc(botMentions.createdAt)], // Chronological order (oldest first)
});
return mentions.map(m => ({
@@ -256,8 +251,8 @@ export async function getUnprocessedMentions(botId: string): Promise<Mention[]>
export async function getAllMentions(botId: string): Promise<Mention[]> {
try {
const mentions = await db.query.botMentions.findMany({
where: eq(botMentions.botId, botId),
orderBy: [desc(botMentions.createdAt)],
where: { botId: botId },
orderBy: () => [desc(botMentions.createdAt)],
});
return mentions.map(m => ({
@@ -307,7 +302,7 @@ export async function getConversationContext(
while (currentPostId && depth < maxDepth) {
const post: any = await db.query.posts.findFirst({
where: eq(posts.id, currentPostId),
where: { id: currentPostId },
with: {
author: {
columns: {
@@ -365,7 +360,7 @@ export async function processMention(mentionId: string): Promise<MentionResponse
try {
// Get the mention
const mention = await db.query.botMentions.findFirst({
where: eq(botMentions.id, mentionId),
where: { id: mentionId },
});
if (!mention) {
@@ -394,7 +389,7 @@ export async function processMention(mentionId: string): Promise<MentionResponse
// Get the bot
const bot = await db.query.bots.findFirst({
where: eq(bots.id, mention.botId),
where: { id: mention.botId },
with: {
user: {
columns: {
@@ -414,7 +409,7 @@ export async function processMention(mentionId: string): Promise<MentionResponse
// Get the mentioning post with author info
const mentionPost = await db.query.posts.findFirst({
where: eq(posts.id, mention.postId),
where: { id: mention.postId },
with: {
author: {
columns: {
@@ -607,7 +602,7 @@ export async function storeMention(data: {
export async function getMentionById(mentionId: string): Promise<Mention | null> {
try {
const mention = await db.query.botMentions.findFirst({
where: eq(botMentions.id, mentionId),
where: { id: mentionId },
});
if (!mention) {
+2 -2
View File
@@ -335,7 +335,7 @@ export function deserializePersonalityConfig(json: string): PersonalityConfig {
*/
export async function getPersonalityConfig(botId: string): Promise<PersonalityConfig | null> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: {
personalityConfig: true,
},
@@ -372,7 +372,7 @@ export async function updatePersonalityConfig(
// Check if bot exists
const existingBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: { id: true },
});
+20 -26
View File
@@ -169,7 +169,7 @@ function getDecryptedApiKeyForBot(bot: typeof bots.$inferSelect): string {
*/
async function getContentItemById(contentItemId: string): Promise<ContentItem | null> {
const item = await db.query.botContentItems.findFirst({
where: eq(botContentItems.id, contentItemId),
where: { id: contentItemId },
});
if (!item) {
@@ -197,10 +197,10 @@ async function getContentItemById(contentItemId: string): Promise<ContentItem |
async function getNextUnprocessedContentItem(botId: string): Promise<ContentItem | null> {
// Get bot's content sources
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
contentSources: {
where: (sources, { eq }) => eq(sources.isActive, true),
where: { isActive: true },
},
user: true,
},
@@ -214,7 +214,7 @@ async function getNextUnprocessedContentItem(botId: string): Promise<ContentItem
// Get ALL posts by this bot to avoid duplicates (no time limit)
const allBotPosts = await db.query.posts.findMany({
where: eq(posts.userId, bot.userId),
where: { userId: bot.userId },
columns: {
linkPreviewUrl: true,
},
@@ -230,10 +230,7 @@ async function getNextUnprocessedContentItem(botId: string): Promise<ContentItem
// Get unprocessed content items from this bot's sources
const items = await db.query.botContentItems.findMany({
where: and(
eq(botContentItems.isProcessed, false),
inArray(botContentItems.sourceId, sourceIds)
),
where: { AND: [{ isProcessed: false }, { sourceId: { in: sourceIds } }] },
orderBy: (items, { asc }) => [asc(items.publishedAt)],
limit: 100, // Get more items to have options after filtering
});
@@ -278,7 +275,7 @@ async function getNextUnprocessedContentItem(botId: string): Promise<ContentItem
async function getBotPreviousPosts(botId: string, limit: number = 40): Promise<string[]> {
// Get bot to find its user ID
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
@@ -287,7 +284,7 @@ async function getBotPreviousPosts(botId: string, limit: number = 40): Promise<s
// Get the bot's recent posts
const recentPosts = await db.query.posts.findMany({
where: eq(posts.userId, bot.userId),
where: { userId: bot.userId },
orderBy: (posts, { desc }) => [desc(posts.createdAt)],
limit,
columns: {
@@ -512,7 +509,7 @@ export async function selectContentForPosting(
// Verify the content belongs to this bot's sources
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
contentSources: true,
user: true,
@@ -691,7 +688,7 @@ async function resolveContentItemSourceUrl(contentItem?: ContentItem | null): Pr
}
const source = await db.query.botContentSources.findFirst({
where: eq(botContentSources.id, contentItem.sourceId),
where: { id: contentItem.sourceId },
columns: {
type: true,
subreddit: true,
@@ -726,7 +723,7 @@ async function createPostInDatabase(
): Promise<typeof posts.$inferSelect> {
// Get bot config
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
@@ -738,7 +735,7 @@ async function createPostInDatabase(
// Get the bot's own user account (not the owner)
const botUser = await db.query.users.findFirst({
where: eq(users.id, bot.userId),
where: { id: bot.userId },
});
if (!botUser) {
@@ -755,7 +752,7 @@ async function createPostInDatabase(
linkPreview = await fetchLinkPreview(sourceUrl);
}
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:43821';
const postUuid = crypto.randomUUID();
try {
@@ -807,7 +804,7 @@ async function federatePost(
try {
// Get bot config
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
});
if (!bot) {
@@ -817,7 +814,7 @@ async function federatePost(
// Get the bot's own user account
const botUser = await db.query.users.findFirst({
where: eq(users.id, bot.userId),
where: { id: bot.userId },
});
if (!botUser) {
@@ -825,7 +822,7 @@ async function federatePost(
return;
}
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
const nodeDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:43821';
// Use swarm delivery for bot posts
const { deliverPostToSwarmFollowers } = await import('@/lib/swarm/interactions');
@@ -993,7 +990,7 @@ export async function triggerPost(
// Get bot from database for generation
const dbBot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: { user: true }, // Required for generatePostContent
});
@@ -1142,7 +1139,7 @@ export async function getPostingStats(botId: string): Promise<{
}> {
// Get bot
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
with: {
contentSources: true,
},
@@ -1160,7 +1157,7 @@ export async function getPostingStats(botId: string): Promise<{
// Get user's post count
const user = await db.query.users.findFirst({
where: eq(users.id, bot.userId),
where: { id: bot.userId },
});
const totalPosts = user?.postsCount || 0;
@@ -1170,10 +1167,7 @@ export async function getPostingStats(botId: string): Promise<{
today.setUTCHours(0, 0, 0, 0);
const postsToday = await db.query.posts.findMany({
where: and(
eq(posts.userId, bot.userId),
// Note: This is a simplified query. In production, you'd want to use a proper date comparison
),
where: { AND: [{ userId: bot.userId }] },
});
// Get content item stats
@@ -1184,7 +1178,7 @@ export async function getPostingStats(botId: string): Promise<{
if (sourceIds.length > 0) {
const allItems = await db.query.botContentItems.findMany({
where: (items, { inArray }) => inArray(items.sourceId, sourceIds),
where: { sourceId: { in: sourceIds } },
});
contentItemsProcessed = allItems.filter(item => item.isProcessed).length;
+5 -21
View File
@@ -129,11 +129,7 @@ async function getOrCreateWindow(
): Promise<typeof botRateLimits.$inferSelect> {
// Try to find existing window
const existing = await db.query.botRateLimits.findFirst({
where: and(
eq(botRateLimits.botId, botId),
eq(botRateLimits.windowType, windowType),
eq(botRateLimits.windowStart, windowStart)
),
where: { AND: [{ botId }, { windowType }, { windowStart: { eq: windowStart } }] },
});
if (existing) {
@@ -158,11 +154,7 @@ async function getOrCreateWindow(
async function getDailyPostCount(botId: string): Promise<number> {
const windowStart = getDailyWindowStart();
const window = await db.query.botRateLimits.findFirst({
where: and(
eq(botRateLimits.botId, botId),
eq(botRateLimits.windowType, 'daily'),
eq(botRateLimits.windowStart, windowStart)
),
where: { AND: [{ botId }, { windowType: 'daily' }, { windowStart: { eq: windowStart } }] },
});
return window?.postCount ?? 0;
@@ -174,11 +166,7 @@ async function getDailyPostCount(botId: string): Promise<number> {
async function getHourlyReplyCount(botId: string): Promise<number> {
const windowStart = getHourlyWindowStart();
const window = await db.query.botRateLimits.findFirst({
where: and(
eq(botRateLimits.botId, botId),
eq(botRateLimits.windowType, 'hourly'),
eq(botRateLimits.windowStart, windowStart)
),
where: { AND: [{ botId }, { windowType: 'hourly' }, { windowStart: { eq: windowStart } }] },
});
return window?.replyCount ?? 0;
@@ -189,7 +177,7 @@ async function getHourlyReplyCount(botId: string): Promise<number> {
*/
async function getLastPostAt(botId: string): Promise<Date | null> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: { lastPostAt: true },
});
@@ -368,11 +356,7 @@ export async function getPostCount(botId: string, windowHours: number): Promise<
// Get all daily windows that overlap with the requested time range
const windows = await db.query.botRateLimits.findMany({
where: and(
eq(botRateLimits.botId, botId),
eq(botRateLimits.windowType, 'daily'),
gte(botRateLimits.windowStart, getDailyWindowStart(windowStart))
),
where: { AND: [{ botId: botId }, { windowType: 'daily' }, { windowStart: { gte: getDailyWindowStart(windowStart) } }] },
});
return windows.reduce((sum, w) => sum + w.postCount, 0);
+7 -25
View File
@@ -679,10 +679,7 @@ export function isDue(
export async function hasUnprocessedContent(botId: string): Promise<boolean> {
// Get all content sources for the bot
const sources = await db.query.botContentSources.findMany({
where: and(
eq(botContentSources.botId, botId),
eq(botContentSources.isActive, true)
),
where: { AND: [{ botId: botId }, { isActive: true }] },
columns: { id: true },
});
@@ -693,10 +690,7 @@ export async function hasUnprocessedContent(botId: string): Promise<boolean> {
// Check if any source has unprocessed content
for (const source of sources) {
const unprocessedItem = await db.query.botContentItems.findFirst({
where: and(
eq(botContentItems.sourceId, source.id),
eq(botContentItems.isProcessed, false)
),
where: { AND: [{ sourceId: source.id }, { isProcessed: false }] },
columns: { id: true },
});
@@ -724,10 +718,7 @@ export async function getNextUnprocessedContent(botId: string): Promise<{
} | null> {
// Get all content sources for the bot
const sources = await db.query.botContentSources.findMany({
where: and(
eq(botContentSources.botId, botId),
eq(botContentSources.isActive, true)
),
where: { AND: [{ botId: botId }, { isActive: true }] },
columns: { id: true },
});
@@ -740,10 +731,7 @@ export async function getNextUnprocessedContent(botId: string): Promise<{
for (const source of sources) {
const item = await db.query.botContentItems.findFirst({
where: and(
eq(botContentItems.sourceId, source.id),
eq(botContentItems.isProcessed, false)
),
where: { AND: [{ sourceId: source.id }, { isProcessed: false }] },
orderBy: (items, { asc }) => [asc(items.publishedAt)],
});
@@ -789,10 +777,7 @@ export async function processScheduledPosts(): Promise<ProcessScheduledPostsResu
// Get all active bots with schedules
const activeBots = await db.query.bots.findMany({
where: and(
eq(bots.isActive, true),
eq(bots.isSuspended, false)
),
where: { AND: [{ isActive: true }, { isSuspended: false }] },
});
for (const bot of activeBots) {
@@ -933,10 +918,7 @@ export async function processScheduledPosts(): Promise<ProcessScheduledPostsResu
*/
async function botHasActiveSources(botId: string): Promise<boolean> {
const sources = await db.query.botContentSources.findMany({
where: and(
eq(botContentSources.botId, botId),
eq(botContentSources.isActive, true)
),
where: { AND: [{ botId: botId }, { isActive: true }] },
columns: { id: true },
});
@@ -951,7 +933,7 @@ async function botHasActiveSources(botId: string): Promise<boolean> {
*/
export async function getBotSchedule(botId: string): Promise<ScheduleConfig | null> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: { scheduleConfig: true },
});
+2 -2
View File
@@ -69,7 +69,7 @@ export async function reinstateBot(botId: string) {
*/
export async function isBotSuspended(botId: string): Promise<boolean> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: { isSuspended: true },
});
@@ -86,7 +86,7 @@ export async function isBotSuspended(botId: string): Promise<boolean> {
*/
export async function ensureBotNotSuspended(botId: string): Promise<void> {
const bot = await db.query.bots.findFirst({
where: eq(bots.id, botId),
where: { id: botId },
columns: { isSuspended: true, suspensionReason: true },
});