feat: add Telegram sticker import functionality
- Implemented TelegramImport component for importing sticker packs from Telegram using a bot token. - Added utility functions for animated sticker conversion (TGS and WEBM to GIF). - Created telegramStickerImport module to handle fetching and processing of Telegram sticker sets. - Introduced GIF.js type definitions for TypeScript support.
This commit is contained in:
@@ -3,6 +3,7 @@ import { Box, Text, IconButton, Icon, Icons, Scroll } from 'folds';
|
||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||
import { GlobalPacks } from './GlobalPacks';
|
||||
import { UserPack } from './UserPack';
|
||||
import { TelegramImport } from './TelegramImport';
|
||||
import { ImagePack } from '../../../plugins/custom-emoji';
|
||||
import { ImagePackView } from '../../../components/image-pack-view';
|
||||
|
||||
@@ -42,6 +43,7 @@ export function EmojisStickers({ requestClose }: EmojisStickersProps) {
|
||||
<Box direction="Column" gap="700">
|
||||
<UserPack onViewPack={setImagePack} />
|
||||
<GlobalPacks onViewPack={setImagePack} />
|
||||
<TelegramImport onViewPack={setImagePack} />
|
||||
</Box>
|
||||
</PageContent>
|
||||
</Scroll>
|
||||
|
||||
167
src/app/features/settings/emojis-stickers/TelegramImport.tsx
Normal file
167
src/app/features/settings/emojis-stickers/TelegramImport.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
Button,
|
||||
Input,
|
||||
Spinner,
|
||||
color,
|
||||
toRem,
|
||||
} from 'folds';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
import {
|
||||
extractPackName,
|
||||
importTelegramStickerPack,
|
||||
} from '../../../utils/telegramStickerImport';
|
||||
import { getUserImagePack, ImagePack } from '../../../plugins/custom-emoji';
|
||||
|
||||
const TELEGRAM_BOT_TOKEN_KEY = 'cinny_telegram_bot_token';
|
||||
|
||||
type TelegramImportProps = {
|
||||
onViewPack?: (pack: ImagePack) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component for importing Telegram sticker packs
|
||||
*/
|
||||
export function TelegramImport({ onViewPack }: TelegramImportProps) {
|
||||
const mx = useMatrixClient();
|
||||
const [botToken, setBotToken] = useState(() =>
|
||||
localStorage.getItem(TELEGRAM_BOT_TOKEN_KEY) || ''
|
||||
);
|
||||
const [packUrl, setPackUrl] = useState('');
|
||||
const [importing, setImporting] = useState(false);
|
||||
const [progress, setProgress] = useState<string>('');
|
||||
const [error, setError] = useState<string>('');
|
||||
const [success, setSuccess] = useState<string>('');
|
||||
|
||||
const handleBotTokenChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const token = e.target.value;
|
||||
setBotToken(token);
|
||||
localStorage.setItem(TELEGRAM_BOT_TOKEN_KEY, token);
|
||||
}, []);
|
||||
|
||||
const handlePackUrlChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPackUrl(e.target.value);
|
||||
setError('');
|
||||
setSuccess('');
|
||||
}, []);
|
||||
|
||||
const handleImport = useCallback(async () => {
|
||||
if (!botToken.trim()) {
|
||||
setError('Please enter your Telegram Bot token');
|
||||
return;
|
||||
}
|
||||
|
||||
const packName = extractPackName(packUrl);
|
||||
if (!packName) {
|
||||
setError('Invalid Telegram sticker pack URL. Use format: t.me/addstickers/PackName');
|
||||
return;
|
||||
}
|
||||
|
||||
setImporting(true);
|
||||
setError('');
|
||||
setSuccess('');
|
||||
setProgress('Starting import...');
|
||||
|
||||
try {
|
||||
const result = await importTelegramStickerPack(
|
||||
mx,
|
||||
botToken.trim(),
|
||||
packUrl.trim(),
|
||||
(current, total, status) => {
|
||||
setProgress(status);
|
||||
}
|
||||
);
|
||||
|
||||
const count = Object.keys(result.content.images || {}).length;
|
||||
setSuccess(`Successfully imported ${count} stickers!`);
|
||||
setPackUrl('');
|
||||
|
||||
// Show the user's pack with the newly imported stickers
|
||||
const userPack = getUserImagePack(mx);
|
||||
if (userPack && onViewPack) {
|
||||
onViewPack(userPack);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to import sticker pack');
|
||||
} finally {
|
||||
setImporting(false);
|
||||
setProgress('');
|
||||
}
|
||||
}, [mx, botToken, packUrl, onViewPack]);
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Import from Telegram</Text>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Telegram Bot Token"
|
||||
description="Create a bot via @BotFather on Telegram to get a token. Your token is stored locally."
|
||||
>
|
||||
<Input
|
||||
type="password"
|
||||
value={botToken}
|
||||
onChange={handleBotTokenChange}
|
||||
placeholder="123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
|
||||
style={{ width: '100%', marginTop: toRem(8) }}
|
||||
disabled={importing}
|
||||
/>
|
||||
</SettingTile>
|
||||
|
||||
<SettingTile
|
||||
title="Sticker Pack URL"
|
||||
description="Paste a Telegram sticker pack link (e.g., t.me/addstickers/PackName)"
|
||||
>
|
||||
<Input
|
||||
value={packUrl}
|
||||
onChange={handlePackUrlChange}
|
||||
placeholder="https://t.me/addstickers/YourPackName"
|
||||
style={{ width: '100%', marginTop: toRem(8) }}
|
||||
disabled={importing}
|
||||
/>
|
||||
</SettingTile>
|
||||
|
||||
{error && (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||
{error}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{success && (
|
||||
<Text size="T200" style={{ color: color.Success.Main }}>
|
||||
{success}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{importing && progress && (
|
||||
<Box gap="200" alignItems="Center">
|
||||
<Spinner size="200" variant="Secondary" />
|
||||
<Text size="T200" priority="300">{progress}</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box justifyContent="End">
|
||||
<Button
|
||||
variant="Secondary"
|
||||
fill="Soft"
|
||||
size="300"
|
||||
radii="300"
|
||||
onClick={handleImport}
|
||||
disabled={importing || !packUrl.trim()}
|
||||
>
|
||||
<Text size="B300">{importing ? 'Importing...' : 'Import Pack'}</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user