Упрощение: общие хелперы, команды и упоминания по entities, фото только для моделей со зрением
- Одна formatUtc и один displayName(profile) вместо копий в разных слоях
- commandOf/mentionsUser/textWithoutMention работают по entities для текста и подписей;
убраны askInCaption, regex-поиск упоминаний и отдельный composer.command("ask")
- Фото скачиваются только если модель их видит; пустой вопрос с картинкой формулирует промпт
- Поддержка фото задаётся явно для моделей по умолчанию, для своей AI_MODEL — через AI_IMAGE_INPUT
- Активность участников считается по сохранённой истории, а не отдельными счётчиками;
один хендлер для message и edited_message, упоминания не пишутся дважды
- SQLite: synchronous = NORMAL для WAL
- Кнопки не-админов отклоняются через bot.drop(isAdmin)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,8 @@ interface ChatMemberServiceDeps {
|
||||
export interface MentionQuery {
|
||||
/** Ники из `@username` без собаки. */
|
||||
usernames: string[];
|
||||
/** Пользователи из упоминаний по имени-ссылке (у них может не быть ника). */
|
||||
profiles: MemberProfile[];
|
||||
/** ID людей, упомянутых по имени-ссылке (у них может не быть ника). */
|
||||
userIds: number[];
|
||||
}
|
||||
|
||||
export interface ResolvedMentions {
|
||||
@@ -37,21 +37,14 @@ const nameMatches = (query: string) => (member: ChatMember) => {
|
||||
.every((token) => memberWords.some((word) => word.startsWith(token)));
|
||||
};
|
||||
|
||||
const uniqueByUser = (members: ChatMember[]): ChatMember[] =>
|
||||
members.filter((member, index) => members.findIndex((m) => m.userId === member.userId) === index);
|
||||
|
||||
export const createChatMemberService = ({ chatMemberRepository }: ChatMemberServiceDeps) => {
|
||||
/** Участник написал сообщение: обновляем имя, ник, счётчик и время активности. */
|
||||
const recordActivity = (chatId: number, profile: MemberProfile, date: number): void =>
|
||||
chatMemberRepository.upsert(chatId, profile, { messages: 1, seenAt: date });
|
||||
|
||||
/** Запоминаем имя и ник без учёта активности (правка сообщения, упоминание по ссылке). */
|
||||
const remember = (chatId: number, profile: MemberProfile): void =>
|
||||
chatMemberRepository.upsert(chatId, profile, { messages: 0, seenAt: 0 });
|
||||
/** Запоминаем актуальные имя и ник: автор сообщения или человек, упомянутый по ссылке. */
|
||||
const remember = (chatId: number, profile: MemberProfile): void => chatMemberRepository.upsert(chatId, profile);
|
||||
|
||||
/** Участники с активностью по сохранённой истории; сначала — недавно писавшие. */
|
||||
const list = (chatId: number): ChatMember[] => chatMemberRepository.listByChat(chatId);
|
||||
|
||||
/** Поиск по нику (с @ или без) или по имени/фамилии; сначала — недавно активные. */
|
||||
/** Поиск по нику (с @ или без) или по имени/фамилии. */
|
||||
const search = (chatId: number, query: string): ChatMember[] => {
|
||||
const cleaned = query.trim().replace(/^@/, "");
|
||||
|
||||
@@ -64,24 +57,21 @@ export const createChatMemberService = ({ chatMemberRepository }: ChatMemberServ
|
||||
};
|
||||
|
||||
/** Превращает упоминания из вопроса в известных участников чата. */
|
||||
const resolveMentions = (chatId: number, { usernames, profiles }: MentionQuery): ResolvedMentions => {
|
||||
profiles.forEach((profile) => remember(chatId, profile));
|
||||
const resolveMentions = (chatId: number, { usernames, userIds }: MentionQuery): ResolvedMentions => {
|
||||
if (usernames.length === 0 && userIds.length === 0) return { members: [], unknownUsernames: [] };
|
||||
|
||||
const members = list(chatId);
|
||||
|
||||
const byUsername = usernames.map((username) => ({ username, member: members.find(usernameIs(username)) }));
|
||||
const unknownUsernames = usernames.filter((username) => !members.some(usernameIs(username)));
|
||||
|
||||
const byProfile = profiles
|
||||
.map((profile) => chatMemberRepository.findById(chatId, profile.userId))
|
||||
.filter((member) => member !== undefined);
|
||||
const mentioned = members.filter(
|
||||
(member) => userIds.includes(member.userId) || usernames.some((username) => usernameIs(username)(member)),
|
||||
);
|
||||
|
||||
return {
|
||||
members: uniqueByUser([...byUsername.flatMap(({ member }) => (member ? [member] : [])), ...byProfile]),
|
||||
unknownUsernames: byUsername.filter(({ member }) => !member).map(({ username }) => username),
|
||||
};
|
||||
return { members: mentioned, unknownUsernames };
|
||||
};
|
||||
|
||||
return { recordActivity, remember, list, search, resolveMentions };
|
||||
return { remember, list, search, resolveMentions };
|
||||
};
|
||||
|
||||
export type ChatMemberService = ReturnType<typeof createChatMemberService>;
|
||||
|
||||
Reference in New Issue
Block a user