Skip to content

Commit

Permalink
release version, 1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
VladImpaler64 committed Oct 8, 2023
1 parent c453490 commit 789d183
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 38 deletions.
34 changes: 18 additions & 16 deletions server/app.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
"use strict";

import "dotenv/config"; // This reads our .env info
import {Markup, Telegraf} from "telegraf"; // Helper for Telegram api
import {message} from "telegraf/filters";
import "dotenv/config"; // A .env file is needed to store our bot token
import { Telegraf } from "telegraf"; // Helper for Telegram bot api
import { message } from "telegraf/filters";
import { makePNG, size } from "./helpers/makePNG.js";

const bot = new Telegraf(process.env.BOT_TOKEN) // Using our bot token, read from dotenv.
const bot = new Telegraf(process.env.BOT_TOKEN)

bot.start((ctx) => ctx.reply('supported commands: \n/editor\n/parseMono ...code')) // For this mini app only one command is needed to start the bot in a private mode
bot.start((ctx) => ctx.reply('supported commands: \n/editor\n/parse ...code')) // "editor" command sends a keyboard button with mini app link, "parse" command parses input text into a png and monospace code

bot.on(message("web_app_data"), async (ctx)=>{ // When we receive a message update containing web_app_data field, we interact with the client this way, it is a simple way but remember the client is closed after the call to Telegram.WebApp.sendData function (client side)
bot.on(message("web_app_data"), async (ctx)=>{ // Call to Telegram.WebApp.sendData function (client side) is received in an update with web_app_data field

await ctx.telegram.sendMessage(ctx.chat.id, `<pre><code>${ctx.message.web_app_data.data}</pre></code>`, {parse_mode: "HTML", reply_markup: {remove_keyboard: true}}); // In my demo app this will send the code made with the client editor in the format of html to where the user opened the webapp
await ctx.telegram.sendMessage(ctx.chat.id, `<pre><code>${ctx.message.web_app_data.data}</pre></code>`, {parse_mode: "HTML", reply_markup: {remove_keyboard: true}}); // This will send the code made with the client editor in the format of html to the user private chat

});

bot.command('editor', async (ctx) => {

if(ctx.chat.type === "private"){ // We make sure it only sends the web app to private chats
if(ctx.chat.type === "private"){ // Make sure it only sends the mini app to private chats
await bot.telegram.sendMessage(ctx.chat.id, `Hello, ${ctx.chat.first_name}, CODEBOX is a simple code editor for sharing code inside Telegram, enjoy!`, {reply_markup: {keyboard: [[{text: "CODEBOX", web_app: {url: "https://amazing-gumption-7b140b.netlify.app/"}}]], resize_keyboard: true, one_time_keyboard: true, input_field_placeholder: "@codebox_robot ...code"}});
} else {
await bot.telegram.sendMessage(ctx.chat.id, "Sorry, can't use this command in a group, try querying the bot, inline-mode, \n`@codebox_bot `", {reply_markup: {remove_keyboard: true}})
await bot.telegram.sendMessage(ctx.chat.id, "Sorry, can't use this command in a group, try querying the bot, inline-mode, \n`@codebox_bot `", {reply_markup: {remove_keyboard: true}}) // We need to open from a keyboard button or inline mode, to have initData available
}
});

bot.command('parse', async (ctx) => {

if(ctx.chat.type === "private"){ // We make sure it only sends the web app to private chats
if(ctx.chat.type === "private"){ // Make sure it only sends the web app to private chats
let text = ctx.message.text.replace("/parse ", "")
if (text.length < 5) return;
// Logic to parse into a png
Expand All @@ -36,23 +36,25 @@ bot.command('parse', async (ctx) => {
}
});

// We show the user the default web app
// We show the user the default mini app when query is empty
let article_id = 0;
bot.on("inline_query", async (ctx)=>{

if (ctx.update.inline_query.query === ''){
return await ctx.answerInlineQuery([], {button: {text: "CODEBOX!, mini app code editor.", web_app: {url: "https://amazing-gumption-7b140b.netlify.app/"}}});
}

// Work with the query input back from mini app (Telegram.WebApp.switchInlineQuery method)
// Work with the query input back from mini app (Telegram.WebApp.switchInlineQuery method), this may cause conflict when user manually inputs data
let text = ctx.update.inline_query.query
if (text.length === 0) return;
// Logic to parse into a png
article_id += 1;
try {

// Logic to parse into a png

try { // Inline queries may fail if user inputs manually the code text, this prevents bot panicking
await makePNG(ctx, text, size(text), bot.telegram, article_id);
} catch (err) {
console.error(err)
console.error(err) // Ignore the error since is unpredictable
}


Expand All @@ -64,7 +66,7 @@ console.log("Bot is running! -", new Date());

// To enable graceful stop
process.once('SIGINT', () => {
// Functions to shutdown services or other kind goes here
// Functions to shutdown services for other kind goes here (Database for example)
bot.stop('SIGINT');
});
process.once('SIGTERM', () => {
Expand Down
6 changes: 3 additions & 3 deletions server/helpers/makePNG.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ html {
padding: 0.5rem;
box-sizing: border-box;
display: contents;
width: ${width * 16 + 64}px;
width: ${(width * 16 + 64) > 1080 ? 1080 : (width * 16 + 64)}px;
height: 0;
background-color: #282c34;
background-color: #282c11;
}
.hljs {
Expand Down Expand Up @@ -124,7 +124,7 @@ html {
html: html
})
.then(async (img) => {
// Image is generated and sent to TG
// Image is generated and sent to user private chat in Telegram
if (bot){
const photo_id = await bot.sendPhoto(ctx.update.inline_query.from.id, { source: img }, {caption: code.length > 1000 ? "Thansk for using code box!" : `<pre><code>${code}</code></pre>`, parse_mode: "HTML"});

Expand Down
3 changes: 1 addition & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { Menu } from "./components/Menu.tsx"
import { Donation } from "./components/Donation.tsx"
import { useTonConnect } from './hooks/useTonConnect.ts'

function App() { // We'll be rendering a two components

function App() { // We'll be rendering a three components

return (
<>
Expand Down
6 changes: 3 additions & 3 deletions src/components/Donation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { useDonationContract } from "../hooks/useDonationContract";

export function Donation(){
const {network, connected} = useTonConnect();
const {donationAddress, balance, sendTon} = useDonationContract();
const {donationAddress, balance, sendTon} = useDonationContract(); // Init the contract when we render this component

function sendDonation(){ // Interaction with the toncoin throught useTonConnect, orbs provider
// Event handlers
function sendDonation(){ // Send donation to contract
let toncoin = document.querySelector("#toncoin") as HTMLInputElement;
console.log(`sent ${toncoin.value}`)
sendTon(toncoin.value as string);
}

Expand Down
8 changes: 4 additions & 4 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ export function Editor() {
setLastChar((e.target as HTMLTextAreaElement).value.charAt(-1))
break;
case "insertFromPaste":
let text = (e.nativeEvent as InputEvent).data;
let text = (e.nativeEvent as InputEvent).data || "";
let i = 1;
for (let letter of text) {
if (letter === '\n') {
(document.querySelector(".editor-numbers") as HTMLTextAreaElement).value += `${lineNumber + i}\n`;
i += 1;
}
}
setLastChar(e.nativeEvent.data.at(-1))
setLastChar((e.nativeEvent as InputEvent).data?.charAt(-1) || lastChar)
setLineNumber(i)
break;

Expand Down Expand Up @@ -123,7 +123,7 @@ export function Editor() {
}

function openPreview(){
document.querySelector("#lang-hg").innerHTML = (document.querySelector(".textcode") as HTMLTextAreaElement).value;
(document.querySelector("#lang-hg") as HTMLDivElement).innerHTML = (document.querySelector(".textcode") as HTMLTextAreaElement).value;
hljs.highlightAll();
let preview = document.querySelector(".bot-info");
(preview as HTMLDialogElement).showModal();
Expand Down Expand Up @@ -189,7 +189,7 @@ export function Editor() {
</div>
<div style={{display: "flex", gap: "2px"}}>
<button onClick={openPreview}>Preview</button>
<button onClick={sendBackToBot}>Parse Telegram</button>
<button onClick={sendBackToBot}>Share code!</button>
</div>
<dialog onClick={exitDialog} className="bot-info">
<div className="preview">
Expand Down
2 changes: 1 addition & 1 deletion src/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function Menu(){
<option value="language-csharp">C# (Windows Java)</option>
<option value="language-sql">SQL</option>
</select>
<div className="github">Visit my github to review the code, get inspired and make a mini app yourself!<br /><a href="https://github.com/VladImpaler64/tg-contest" style={{borderRadius: "1rem"}}>https://github.com/VladImpaler64/tg-contest</a></div>
<div className="github">Visit my github to review the code, get inspired and make a mini app yourself!<br /><a href="https://github.com/VladImpaler64/tg-contest" style={{borderRadius: "1rem"}}>github.com/VladImpaler64/</a><br />Special thanks to <br /><a href="https://github.com/SalamandraDevs">Salamandra devs</a><br />for hosting the bot</div>
</div>
<div className="menu-container" onClick={onClickContainer}></div>
</>);
Expand Down
10 changes: 4 additions & 6 deletions src/components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function Nav(){
let reader = new FileReader();
reader.onload = ()=>{
(buffer as HTMLTextAreaElement).value = reader.result as string;
// Insert number of lines
// Insert number of lines when reading a file
let i = 1, total = "";
for (let letter of reader.result as string) {
if (letter === '\n') {
Expand All @@ -23,20 +23,18 @@ export function Nav(){
}
}

document.querySelector(".editor-numbers").value = total;
(document.querySelector(".editor-numbers") as HTMLTextAreaElement).value = total;
}
reader.readAsText(f[0])
}

// Functionality with Telegram
const webapp = window.Telegram.WebApp;
// const user_info = Telegram.Utils.urlParseQueryString(Telegram.WebApp.initData);

function clickX(){
// let username = Telegram.Utils.urlParseQueryString(user_info.user)
let data = (document.querySelector(".textcode") as HTMLTextAreaElement).value;
if (data.length > 0 && data.length <= 4096){
webapp.CloudStorage.setItem("buffer_data", data, (err, stored)=>{
webapp.CloudStorage.setItem("buffer_data", data, (err, stored)=>{ // Store last input data in cloudstorage, loaded on mini app init
console.log(err, stored)
});
}
Expand All @@ -48,7 +46,7 @@ export function Nav(){
let lang = (document.querySelector("#langs") as HTMLInputElement).value;

let data = JSON.stringify({color: color, font_size: font, lang: lang});
webapp.CloudStorage.setItem("config", data, (err, stored)=>{
webapp.CloudStorage.setItem("config", data, (err, stored)=>{ // Store config data to load on mini app init
console.log(err, stored)
});
} catch (err) {
Expand Down
27 changes: 24 additions & 3 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { TonConnectUIProvider } from "@tonconnect/ui-react"; // This will help us to connect to TON blockchain with ton connect protocol, it's very handy
import { TonConnectUIProvider } from "@tonconnect/ui-react"; // Connection provider element to TON blockchain with ton connect protocol
const manifest = "https://raw.githubusercontent.com/ton-community/tutorials/main/03-client/test/public/tonconnect-manifest.json"; // Manifest must public, it is a description on your contract for it to appear in the ton connect protocol, I'll be using the example manifest

// This is a simple react template all the components and functions are in component folder, this will start the process of rendering our components

// Once DOM is fully loaded we start making all web app function calls
document.addEventListener("DOMContentLoaded", ()=>{
// Docs: https://core.telegram.org/bots/webapps#initializing-mini-apps

let webapp = window.Telegram.WebApp; // For convinient calling the webapp interface
webapp.expand(); // This method will spand the webapp, it's convinient if your webapp needs all the space available
// webapp.BackButton.show(); // This method makes a callback and when the user press the backbutton is called
webapp.enableClosingConfirmation();


// Once all DOM is created we log in last code and config from cloudStorage
try {

Expand Down Expand Up @@ -48,8 +47,30 @@ document.addEventListener("DOMContentLoaded", ()=>{

// Theming the mini app to the user looks
console.log(webapp.colorScheme, webapp.themeParams); // We separete our styling in two main modes, light and dark
// If your app needs a personalization make use of themeParams, I am not doing any.

// All event handlers for mini app
/*
List of events
themeChanged
viewportChanged
mainButtonClicked
backButtonClicked
settingsButtonClicked
invoiceClosed
popupClosed
qrTextReceived
clipboardTextReceived
writeAccessRequested
contactRequested
Example
webapp.onEvent("backButtonClicked", ()=>{
// Logic
});
*/

});

Expand Down

0 comments on commit 789d183

Please sign in to comment.