fix mini app bot father
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { Type } from 'class-transformer';
|
||||||
import { IsBoolean, IsInt, IsOptional, IsString, IsUrl, Max, Min, MinLength } from 'class-validator';
|
import { IsBoolean, IsInt, IsOptional, IsString, IsUrl, Max, Min, MinLength } from 'class-validator';
|
||||||
|
|
||||||
export class CreateBotDto {
|
export class CreateBotDto {
|
||||||
@@ -65,11 +66,13 @@ export class ListAdminBotsQueryDto {
|
|||||||
search?: string;
|
search?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
page?: number;
|
page?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@Max(100)
|
@Max(100)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { Check, Copy, Loader2, Bot } from 'lucide-react';
|
import { Check, Copy, Loader2, Bot, ArrowLeft } from 'lucide-react';
|
||||||
import { useToast } from '@/components/id/toast-provider';
|
import { useToast } from '@/components/id/toast-provider';
|
||||||
import { usePublicSettings } from '@/components/id/public-settings-provider';
|
import { usePublicSettings } from '@/components/id/public-settings-provider';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -12,7 +12,12 @@ import { cn } from '@/lib/utils';
|
|||||||
|
|
||||||
type Step = 'name' | 'username' | 'done';
|
type Step = 'name' | 'username' | 'done';
|
||||||
|
|
||||||
export function BotCreateMiniAppContent() {
|
interface BotCreateMiniAppContentProps {
|
||||||
|
onBack?: () => void;
|
||||||
|
onCreated?: (botId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BotCreateMiniAppContent({ onBack, onCreated }: BotCreateMiniAppContentProps = {}) {
|
||||||
const { projectName } = usePublicSettings();
|
const { projectName } = usePublicSettings();
|
||||||
const { effectiveToken, canUse, authReady, waitingForBridge, isLoading } = useMiniAppAuth();
|
const { effectiveToken, canUse, authReady, waitingForBridge, isLoading } = useMiniAppAuth();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@@ -84,6 +89,11 @@ export function BotCreateMiniAppContent() {
|
|||||||
<div className="min-h-screen bg-[#17212b] p-4 text-white">
|
<div className="min-h-screen bg-[#17212b] p-4 text-white">
|
||||||
<div className="mx-auto max-w-md space-y-5 rounded-[24px] bg-[#242f3d] p-5 shadow-xl">
|
<div className="mx-auto max-w-md space-y-5 rounded-[24px] bg-[#242f3d] p-5 shadow-xl">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
{onBack ? (
|
||||||
|
<Button type="button" variant="ghost" size="icon" className="h-9 w-9 shrink-0 text-[#8b93a7] hover:bg-[#17212b] hover:text-white" onClick={onBack}>
|
||||||
|
<ArrowLeft className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-[#3390ec]/20 text-[#3390ec]">
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-[#3390ec]/20 text-[#3390ec]">
|
||||||
<Bot className="h-6 w-6" />
|
<Bot className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
@@ -184,6 +194,11 @@ export function BotCreateMiniAppContent() {
|
|||||||
>
|
>
|
||||||
Создать ещё одного бота
|
Создать ещё одного бота
|
||||||
</Button>
|
</Button>
|
||||||
|
{onCreated && createdBot.botId ? (
|
||||||
|
<Button className="w-full rounded-xl" onClick={() => onCreated(createdBot.botId)}>
|
||||||
|
Настройки бота
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Suspense } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { BotCreateMiniAppContent } from './content';
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function BotCreateLegacyRedirectPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
router.replace('/mini-apps/bot-father?mode=create');
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
export default function BotCreateMiniAppPage() {
|
|
||||||
return (
|
return (
|
||||||
<Suspense
|
<div className="flex min-h-screen items-center justify-center p-6 text-sm text-[#667085]">
|
||||||
fallback={
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
<div className="flex min-h-screen items-center justify-center p-6 text-sm text-[#667085]">Загрузка...</div>
|
Перенаправление...
|
||||||
}
|
</div>
|
||||||
>
|
|
||||||
<BotCreateMiniAppContent />
|
|
||||||
</Suspense>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
171
apps/frontend/app/mini-apps/bot-father/content.tsx
Normal file
171
apps/frontend/app/mini-apps/bot-father/content.tsx
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
|
import { Bot, ChevronRight, Loader2, Plus } from 'lucide-react';
|
||||||
|
import { usePublicSettings } from '@/components/id/public-settings-provider';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { BotCreateMiniAppContent } from '@/app/mini-apps/bot-create/content';
|
||||||
|
import { BotManageMiniAppContent } from '@/app/mini-apps/bot-manage/content';
|
||||||
|
import { useMiniAppAuth } from '@/hooks/use-mini-app-auth';
|
||||||
|
import { fetchMyBots, getApiErrorMessage, type ManagedBot } from '@/lib/api';
|
||||||
|
import { useToast } from '@/components/id/toast-provider';
|
||||||
|
|
||||||
|
type View = 'list' | 'create' | 'manage';
|
||||||
|
|
||||||
|
export function BotFatherMiniAppContent() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const router = useRouter();
|
||||||
|
const { projectName } = usePublicSettings();
|
||||||
|
const { showToast } = useToast();
|
||||||
|
const { effectiveToken, canUse, authReady, waitingForBridge, isLoading } = useMiniAppAuth();
|
||||||
|
|
||||||
|
const initialBotId = searchParams.get('botId');
|
||||||
|
const initialMode = searchParams.get('mode');
|
||||||
|
|
||||||
|
const [view, setView] = useState<View>(() => {
|
||||||
|
if (initialBotId) return 'manage';
|
||||||
|
if (initialMode === 'create') return 'create';
|
||||||
|
return 'list';
|
||||||
|
});
|
||||||
|
const [selectedBotId, setSelectedBotId] = useState<string | null>(initialBotId);
|
||||||
|
const [bots, setBots] = useState<ManagedBot[]>([]);
|
||||||
|
const [loadingBots, setLoadingBots] = useState(false);
|
||||||
|
|
||||||
|
const syncUrl = useCallback(
|
||||||
|
(nextView: View, botId?: string | null) => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (nextView === 'create') params.set('mode', 'create');
|
||||||
|
if (nextView === 'manage' && botId) params.set('botId', botId);
|
||||||
|
const query = params.toString();
|
||||||
|
router.replace(query ? `/mini-apps/bot-father?${query}` : '/mini-apps/bot-father');
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadBots = useCallback(async () => {
|
||||||
|
if (!effectiveToken) return;
|
||||||
|
setLoadingBots(true);
|
||||||
|
try {
|
||||||
|
const response = await fetchMyBots(effectiveToken);
|
||||||
|
setBots((response.bots ?? []).filter((bot) => !bot.isSystemBot));
|
||||||
|
} catch (error) {
|
||||||
|
showToast(getApiErrorMessage(error, 'Не удалось загрузить список ботов') ?? 'Ошибка');
|
||||||
|
} finally {
|
||||||
|
setLoadingBots(false);
|
||||||
|
}
|
||||||
|
}, [effectiveToken, showToast]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canUse) return;
|
||||||
|
if (view === 'list') void loadBots();
|
||||||
|
}, [canUse, loadBots, view]);
|
||||||
|
|
||||||
|
const selectedBot = useMemo(
|
||||||
|
() => bots.find((bot) => bot.id === selectedBotId) ?? null,
|
||||||
|
[bots, selectedBotId]
|
||||||
|
);
|
||||||
|
|
||||||
|
function openManage(botId: string) {
|
||||||
|
setSelectedBotId(botId);
|
||||||
|
setView('manage');
|
||||||
|
syncUrl('manage', botId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreate() {
|
||||||
|
setView('create');
|
||||||
|
syncUrl('create');
|
||||||
|
}
|
||||||
|
|
||||||
|
function backToList() {
|
||||||
|
setSelectedBotId(null);
|
||||||
|
setView('list');
|
||||||
|
syncUrl('list');
|
||||||
|
void loadBots();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((isLoading || waitingForBridge || !authReady) && !effectiveToken) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center p-6 text-center text-sm text-[#667085]">
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Загрузка...
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canUse) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen items-center justify-center p-6 text-center text-sm text-[#667085]">
|
||||||
|
Войдите в {projectName}, чтобы управлять ботами
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view === 'create') {
|
||||||
|
return (
|
||||||
|
<BotCreateMiniAppContent
|
||||||
|
onBack={backToList}
|
||||||
|
onCreated={(botId) => openManage(botId)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view === 'manage' && selectedBotId) {
|
||||||
|
return <BotManageMiniAppContent botId={selectedBotId} onBack={backToList} botHint={selectedBot?.username} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-[#17212b] p-4 text-white">
|
||||||
|
<div className="mx-auto max-w-md space-y-4">
|
||||||
|
<div className="rounded-[24px] bg-[#242f3d] p-5 shadow-xl">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-[#3390ec]/20 text-[#3390ec]">
|
||||||
|
<Bot className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 className="text-lg font-semibold">BotFather</h1>
|
||||||
|
<p className="text-sm text-[#8b93a7]">Создание и настройка ваших ботов</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button className="h-12 w-full rounded-xl text-base" onClick={openCreate}>
|
||||||
|
<Plus className="mr-2 h-5 w-5" />
|
||||||
|
Создать нового бота
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="rounded-[24px] bg-[#242f3d] p-4 shadow-xl">
|
||||||
|
<p className="mb-3 text-sm font-medium text-[#c5cad3]">Мои боты</p>
|
||||||
|
{loadingBots ? (
|
||||||
|
<div className="flex items-center justify-center gap-2 py-8 text-sm text-[#8b93a7]">
|
||||||
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
Загрузка...
|
||||||
|
</div>
|
||||||
|
) : bots.length === 0 ? (
|
||||||
|
<p className="py-6 text-center text-sm text-[#8b93a7]">У вас пока нет ботов</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{bots.map((bot) => (
|
||||||
|
<button
|
||||||
|
key={bot.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => openManage(bot.id)}
|
||||||
|
className="flex w-full items-center gap-3 rounded-2xl bg-[#17212b] px-4 py-3 text-left transition hover:bg-[#1c2733]"
|
||||||
|
>
|
||||||
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-[#3390ec]/15 text-[#3390ec]">
|
||||||
|
<Bot className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<p className="truncate font-medium">{bot.name}</p>
|
||||||
|
<p className="truncate text-sm text-[#8b93a7]">@{bot.username}</p>
|
||||||
|
</div>
|
||||||
|
<ChevronRight className="h-5 w-5 shrink-0 text-[#667085]" />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
20
apps/frontend/app/mini-apps/bot-father/page.tsx
Normal file
20
apps/frontend/app/mini-apps/bot-father/page.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Suspense } from 'react';
|
||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
import { BotFatherMiniAppContent } from './content';
|
||||||
|
|
||||||
|
export default function BotFatherMiniAppPage() {
|
||||||
|
return (
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="flex min-h-screen items-center justify-center p-6 text-sm text-[#667085]">
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Загрузка...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<BotFatherMiniAppContent />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { Copy, ImageIcon, LayoutGrid, Loader2, RefreshCw, TextQuote, UserCircle2 } from 'lucide-react';
|
import { Copy, ImageIcon, LayoutGrid, Loader2, RefreshCw, TextQuote, UserCircle2, ArrowLeft } from 'lucide-react';
|
||||||
import { usePublicSettings } from '@/components/id/public-settings-provider';
|
import { usePublicSettings } from '@/components/id/public-settings-provider';
|
||||||
import { useToast } from '@/components/id/toast-provider';
|
import { useToast } from '@/components/id/toast-provider';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -75,9 +75,17 @@ function SectionCard({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BotManageMiniAppContent() {
|
export function BotManageMiniAppContent({
|
||||||
|
botId: botIdProp,
|
||||||
|
onBack,
|
||||||
|
botHint
|
||||||
|
}: {
|
||||||
|
botId?: string;
|
||||||
|
onBack?: () => void;
|
||||||
|
botHint?: string;
|
||||||
|
} = {}) {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const botId = searchParams.get('botId') ?? '';
|
const botId = botIdProp ?? searchParams.get('botId') ?? '';
|
||||||
const { projectName } = usePublicSettings();
|
const { projectName } = usePublicSettings();
|
||||||
const { effectiveToken, canUse: canUseSession, authReady, waitingForBridge, isLoading } = useMiniAppAuth();
|
const { effectiveToken, canUse: canUseSession, authReady, waitingForBridge, isLoading } = useMiniAppAuth();
|
||||||
const { showToast } = useToast();
|
const { showToast } = useToast();
|
||||||
@@ -239,8 +247,20 @@ export function BotManageMiniAppContent() {
|
|||||||
<div className="min-h-screen bg-[#f4f5f8] p-4 pb-8">
|
<div className="min-h-screen bg-[#f4f5f8] p-4 pb-8">
|
||||||
<div className="mx-auto max-w-lg space-y-4">
|
<div className="mx-auto max-w-lg space-y-4">
|
||||||
<div className="rounded-[24px] bg-white p-5 shadow-sm">
|
<div className="rounded-[24px] bg-white p-5 shadow-sm">
|
||||||
<h1 className="text-lg font-semibold">Настройки бота</h1>
|
<div className="flex items-start gap-3">
|
||||||
<p className="text-sm text-[#667085]">Интерактивное управление профилем — аналог команд BotFather</p>
|
{onBack ? (
|
||||||
|
<Button type="button" variant="ghost" size="icon" className="mt-0.5 h-9 w-9 shrink-0" onClick={onBack}>
|
||||||
|
<ArrowLeft className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
<div>
|
||||||
|
<h1 className="text-lg font-semibold">Настройки бота</h1>
|
||||||
|
<p className="text-sm text-[#667085]">
|
||||||
|
{botHint ? `@${botHint.replace(/_bot$/i, '')}_bot · ` : ''}
|
||||||
|
Полное управление как в BotFather
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|||||||
@@ -1,20 +1,40 @@
|
|||||||
import { Suspense } from 'react';
|
'use client';
|
||||||
import { Loader2 } from 'lucide-react';
|
|
||||||
import { BotManageMiniAppContent } from './content';
|
import { useEffect } from 'react';
|
||||||
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
|
function BotManageLegacyRedirectContent() {
|
||||||
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const botId = searchParams.get('botId');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const target = botId
|
||||||
|
? `/mini-apps/bot-father?botId=${encodeURIComponent(botId)}`
|
||||||
|
: '/mini-apps/bot-father';
|
||||||
|
router.replace(target);
|
||||||
|
}, [botId, router]);
|
||||||
|
|
||||||
function BotManageFallback() {
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center p-6 text-sm text-[#667085]">
|
<div className="flex min-h-screen items-center justify-center p-6 text-sm text-[#667085]">
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
Загрузка Mini App...
|
Перенаправление...
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BotManageMiniAppPage() {
|
export default function BotManageLegacyRedirectPage() {
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<BotManageFallback />}>
|
<Suspense
|
||||||
<BotManageMiniAppContent />
|
fallback={
|
||||||
|
<div className="flex min-h-screen items-center justify-center p-6 text-sm text-[#667085]">
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<BotManageLegacyRedirectContent />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -365,10 +365,17 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
|
|||||||
const loadMessages = useCallback(async (roomId: string) => {
|
const loadMessages = useCallback(async (roomId: string) => {
|
||||||
if (!token || isPinLocked) return;
|
if (!token || isPinLocked) return;
|
||||||
const room = rooms.find((item) => item.id === roomId);
|
const room = rooms.find((item) => item.id === roomId);
|
||||||
if (room?.type === 'BOT') return;
|
if (!room || room.type === 'BOT') return;
|
||||||
const response = await fetchChatMessages(roomId, token);
|
try {
|
||||||
setMessages((response.messages ?? []).filter((message) => !message.isDeleted));
|
const response = await fetchChatMessages(roomId, token);
|
||||||
}, [isPinLocked, rooms, token]);
|
setMessages((response.messages ?? []).filter((message) => !message.isDeleted));
|
||||||
|
} catch (error) {
|
||||||
|
const message = getApiErrorMessage(error, 'Не удалось загрузить сообщения');
|
||||||
|
if (message && !message.includes('Bot API')) {
|
||||||
|
showToast(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isPinLocked, rooms, showToast, token]);
|
||||||
|
|
||||||
const loadPresence = useCallback(async () => {
|
const loadPresence = useCallback(async () => {
|
||||||
if (!token || isPinLocked) return;
|
if (!token || isPinLocked) return;
|
||||||
@@ -406,10 +413,11 @@ export function FamilyGroupView({ groupId }: { groupId: string }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeRoomId || activeRoomIsBot) return;
|
if (!activeRoomId || activeRoomIsBot) return;
|
||||||
|
if (!rooms.some((room) => room.id === activeRoomId)) return;
|
||||||
void loadMessages(activeRoomId);
|
void loadMessages(activeRoomId);
|
||||||
setEditingMessageId(null);
|
setEditingMessageId(null);
|
||||||
setDraft('');
|
setDraft('');
|
||||||
}, [activeRoomId, activeRoomIsBot, loadMessages]);
|
}, [activeRoomId, activeRoomIsBot, loadMessages, rooms]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeRoomId || !token || !messages.length) return;
|
if (!activeRoomId || !token || !messages.length) return;
|
||||||
|
|||||||
@@ -394,7 +394,10 @@ export function MiniFamilyChat() {
|
|||||||
exitSelectionMode();
|
exitSelectionMode();
|
||||||
showToast(ids.length === 1 ? 'Сообщение удалено' : 'Сообщения удалены');
|
showToast(ids.length === 1 ? 'Сообщение удалено' : 'Сообщения удалены');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (activeRoom) {
|
if (view === 'bot' && activeBot) {
|
||||||
|
const response = await fetchBotChatMessages(activeBot.botRef, token);
|
||||||
|
setBotMessages(response.messages ?? []);
|
||||||
|
} else if (activeRoom && activeRoom.type !== 'BOT') {
|
||||||
const response = await fetchChatMessages(activeRoom.id, token);
|
const response = await fetchChatMessages(activeRoom.id, token);
|
||||||
setMessages((response.messages ?? []).filter((message) => !message.isDeleted));
|
setMessages((response.messages ?? []).filter((message) => !message.isDeleted));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1160,7 +1160,13 @@ export class AuthGrpcController {
|
|||||||
|
|
||||||
@GrpcMethod('BotService', 'ListAllBots')
|
@GrpcMethod('BotService', 'ListAllBots')
|
||||||
listAllBots(command: { requesterId: string; isSuperAdmin?: boolean; search?: string; page?: number; limit?: number }) {
|
listAllBots(command: { requesterId: string; isSuperAdmin?: boolean; search?: string; page?: number; limit?: number }) {
|
||||||
return this.botFather.listAllBots(command.requesterId, Boolean(command.isSuperAdmin), command.search, command.page, command.limit);
|
return this.botFather.listAllBots(
|
||||||
|
command.requesterId,
|
||||||
|
Boolean(command.isSuperAdmin),
|
||||||
|
command.search,
|
||||||
|
Number(command.page) || 1,
|
||||||
|
Number(command.limit) || 20
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GrpcMethod('BotService', 'SetBotActive')
|
@GrpcMethod('BotService', 'SetBotActive')
|
||||||
|
|||||||
@@ -9,15 +9,19 @@ export function resolvePublicFrontendUrl() {
|
|||||||
return (process.env.PUBLIC_FRONTEND_URL ?? 'http://localhost:3002').replace(/\/$/, '');
|
return (process.env.PUBLIC_FRONTEND_URL ?? 'http://localhost:3002').replace(/\/$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildBotFatherWebAppUrl() {
|
||||||
|
return `${resolvePublicFrontendUrl()}/mini-apps/bot-father`;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildBotManageWebAppUrl(botId: string) {
|
export function buildBotManageWebAppUrl(botId: string) {
|
||||||
return `${resolvePublicFrontendUrl()}/mini-apps/bot-manage?botId=${botId}`;
|
return `${resolvePublicFrontendUrl()}/mini-apps/bot-father?botId=${encodeURIComponent(botId)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildBotCreateWebAppUrl() {
|
export function buildBotCreateWebAppUrl() {
|
||||||
return `${resolvePublicFrontendUrl()}/mini-apps/bot-create`;
|
return `${buildBotFatherWebAppUrl()}?mode=create`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBotManageWebAppUrl(url: string | null | undefined) {
|
export function isBotManageWebAppUrl(url: string | null | undefined) {
|
||||||
if (!url?.trim()) return false;
|
if (!url?.trim()) return false;
|
||||||
return url.includes('/mini-apps/bot-manage');
|
return url.includes('/mini-apps/bot-manage') || url.includes('/mini-apps/bot-father');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BOTFATHER_BOT_USERNAME, buildBotCreateWebAppUrl, isBotManageWebAppUrl } from './bot-father.constants';
|
import { BOTFATHER_BOT_USERNAME, buildBotFatherWebAppUrl, isBotManageWebAppUrl } from './bot-father.constants';
|
||||||
|
|
||||||
export type TelegramMenuButtonDefault = { type: 'default' };
|
export type TelegramMenuButtonDefault = { type: 'default' };
|
||||||
export type TelegramMenuButtonCommands = { type: 'commands' };
|
export type TelegramMenuButtonCommands = { type: 'commands' };
|
||||||
@@ -141,11 +141,11 @@ export function resolveComposerMenuButton(options: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.bot.isSystemBot || options.bot.username === BOTFATHER_BOT_USERNAME) {
|
if (options.bot.isSystemBot || options.bot.username === BOTFATHER_BOT_USERNAME) {
|
||||||
const url = buildBotCreateWebAppUrl();
|
const url = buildBotFatherWebAppUrl();
|
||||||
return {
|
return {
|
||||||
menuButton: { type: 'web_app', text: 'Создать бота', web_app: { url } },
|
menuButton: { type: 'web_app', text: 'BotFather', web_app: { url } },
|
||||||
composerWebAppUrl: url,
|
composerWebAppUrl: url,
|
||||||
buttonText: 'Создать бота'
|
buttonText: 'BotFather'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user