feat(api): allow bots to search messages within a single community (#1660)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-15 23:11:45 +02:00
1 parent b5285019cd
commit 490b710c61
42 files changed
+113 -75

No files matched your search

@@ -23,6 +23,7 @@ import type {Channel} from '../models/Channel';
import {getMessageSearchService} from '../SearchFactory';
import type {IUserRepository} from '../user/IUserRepository';
import {canUserAccessNsfwContent} from '../utils/AgeUtils';
import {mapWithConcurrency} from '../utils/ConcurrencyUtils';
import type {WorkerTaskName} from '../worker/WorkerLaneConfig';
import {buildMessageSearchFilters} from './BuildMessageSearchFilters';
import {channelNeedsReindexing} from './ChannelIndexingUtils';
@@ -270,21 +271,3 @@ export class GlobalSearchService {
return canUserAccessNsfwContent(user);
}
}
async function mapWithConcurrency<T, TResult>(
items: ReadonlyArray<T>,
concurrency: number,
mapper: (item: T, index: number) => Promise<TResult>,
): Promise<Array<TResult>> {
const results = new Array<TResult>(items.length);
let nextIndex = 0;
async function worker(): Promise<void> {
for (;;) {
const index = nextIndex++;
if (index >= items.length) return;
results[index] = await mapper(items[index]!, index);
}
}
await Promise.all(Array.from({length: Math.min(concurrency, items.length)}, () => worker()));
return results;
}
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {ValidationErrorCodes} from '@fluxer/constants/src/ValidationErrorCodes';
import {InputValidationError} from '@fluxer/errors/src/domains/core/InputValidationError';
import {GlobalSearchMessagesRequest} from '@fluxer/schema/src/domains/message/MessageRequestSchemas';
import {MessageSearchResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
import {DefaultUserOnly, LoginRequired} from '../../middleware/AuthMiddleware';
import {LoginRequired} from '../../middleware/AuthMiddleware';
import {RateLimitMiddleware} from '../../middleware/RateLimitMiddleware';
import {OpenAPI} from '../../middleware/ResponseTypeMiddleware';
import {RateLimitConfigs} from '../../RateLimitConfig';
@@ -14,11 +16,11 @@ export function SearchController(app: HonoApp) {
'/search/messages',
RateLimitMiddleware(RateLimitConfigs.SEARCH_MESSAGES),
LoginRequired,
DefaultUserOnly,
OpenAPI({
operationId: 'search_messages',
summary: 'Search messages',
description: 'Searches for messages across guilds and channels accessible to the authenticated user.',
description:
'Searches for messages across guilds and channels accessible to the authenticated user. Bots must supply a context guild or channel and may only use the current scope.',
responseSchema: MessageSearchResponse,
statusCode: 200,
security: ['bearerToken', 'sessionToken'],
@@ -27,7 +29,11 @@ export function SearchController(app: HonoApp) {
Validator('json', GlobalSearchMessagesRequest),
async (ctx) => {
const params = ctx.req.valid('json');
const userId = ctx.get('user').id;
const user = ctx.get('user');
if (user.isBot && (params.scope ?? 'current') !== 'current') {
throw InputValidationError.fromCode('scope', ValidationErrorCodes.BOT_SEARCH_SCOPE_UNAVAILABLE);
}
const userId = user.id;
const requestCache = ctx.get('requestCache');
const result = await ctx.get('searchService').searchMessages({userId, requestCache, data: params});
return ctx.json(result);