Samuel Edusa MD
Building a Paste App
Samuel Edusa, MD | Oct 20, 2024
I kept running into the same problem: I needed to get a code snippet, URL, or quick note from my computer to my phone, and the usual methods were annoying. So I built a small paste app that sends text from a website straight to my phone via Telegram. Here's how I put it together.
The concept
Simple: a web page where I type a title and message, hit send, and it shows up on my phone via Telegram. Telegram has a solid bot API, so I used that as the delivery mechanism.
A sketch of the application flow
Tech stack
- React for the UI
- styled-components for styling
- Telegram Bot API for message delivery
Key Components
1. User Interface
The UI consists of a sticky note-like component with two main input fields:
- A title input (
NoteInput) - A message textarea (
NoteTextarea)
I used styled-components to create a visually appealing and responsive design that works well on both desktop and mobile devices.
User interface for the paste app
2. State Management
I utilized React's useState hook to manage the state of the title and message inputs:
const [title, setTitle] = useState(''); const [message, setMessage] = useState('');
3. Date and Time Display
To add context to each paste, I implemented a real-time date and time display using the useEffect hook:
useEffect(() => { const updateDateTime = () => { const now = new Date(); setCurrentDateTime(now.toLocaleString()); }; updateDateTime(); const timer = setInterval(updateDateTime, 1000); return () => clearInterval(timer); }, []);
4. Sending Messages to Telegram
The core functionality is implemented in the sendToTelegram function. This function:
- Cleans and escapes the input text to prevent XSS attacks
- Formats the message with HTML tags for better readability
- Sends a POST request to the Telegram Bot API.
NOTE: Avoid exposing sensitive credentials (token and chat_id) in client-side code. Instead of making direct API calls to Telegram from the frontend, implement a secure middleware layer - either a proxy server or backend API - to handle authentication and protect your secrets. In this implementation, I used Netlify Functions as a serverless solution to securely manage API calls.
sendToTelegram (front end code)
const sendToTelegram = () => { // Clean and escape the title and message const cleanedTitle = cleanText(title); const cleanedMessage = cleanText(message); const escapedTitle = escapeHtml(cleanedTitle); const escapedMessage = escapeHtml(cleanedMessage); fetch('/.netlify/functions/send-telegram', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title: escapedTitle, message: escapedMessage, dateTime: currentDateTime, }), }) .then((response) => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then((data) => { if (data.success) { setTitle(''); setMessage(''); alert('Message sent successfully!'); } else { throw new Error(data.error || 'Unknown error occurred'); } }) .catch((error) => { console.error('Error sending message:', error); alert(`Error sending message: ${error.message}`); }); };
send-telegram (serverless function)
const axios = require('axios'); exports.handler = async function(event, context) { if (event.httpMethod !== 'POST') { return { statusCode: 405, body: 'Method Not Allowed' }; } const { title, message, dateTime } = JSON.parse(event.body); const telegram_bot_token = process.env.TELEGRAM_BOT_TOKEN; const telegram_chat_id = process.env.TELEGRAM_CHAT_ID; if (!telegram_bot_token || !telegram_chat_id) { return { statusCode: 500, body: JSON.stringify({ error: 'Missing Telegram configuration' }) }; } const telegram_message = `<b>${title}</b>\n\n<pre><code>${message}</code></pre>\n\n<i>${dateTime}</i>`; try { const response = await axios.post(`https://api.telegram.org/bot${telegram_bot_token}/sendMessage`, { chat_id: telegram_chat_id, text: telegram_message, parse_mode: 'HTML' }); if (response.data.ok) { return { statusCode: 200, body: JSON.stringify({ success: true }) }; } else { throw new Error(response.data.description || 'Unknown error occurred'); } } catch (error) { console.error('Error sending message:', error); return { statusCode: 500, body: JSON.stringify({ error: `Error sending message: ${error.message}` }) }; } };
Screenshot of message being sent to Telegram chat
Note sent to private Telegram chat with message body formatted to allow easier copying of code snippets
5. Security Considerations
For security, I added two helper functions:
cleanText: Removes non-printable characters (except newlines and spaces) and trims the inputescapeHtml: Escapes special HTML characters to prevent XSS attacks
What's next
The app works well for my daily needs. If I come back to it, I'd probably add:
- Authentication so it's not just secured by obscurity
- A history view for past pastes
- File upload support
