📜 ⬆️ ⬇️

The recipe for developing a bot under Telegram



Good afternoon, dear readers of Habrahabr!

In this topic, I want to share with you the experience of developing a bot under Telegram for 4 days. This bot translates all voice messages it receives into text. I tried to do it quickly, but qualitatively - I learned a couple of technologies. I will try to describe my process of overcoming mistakes and barriers in as much detail as possible; to prove that, even without the necessary skills, to launch your product is not so difficult.
')
The article may be interesting to both newcomers to programming - to see how many obstacles stand in the way of the finished product, and to more advanced specialists - to laugh somewhere, cry somewhere, write a “vital” comment somewhere.

Preamble


And so, what can a programmer do in 4 days?

Write a module, write one screen of an iOS application, write 15% of a complex bot under Telegram, raise or lower MMR in Dota2 or rank in Overwatch. He can spend the weekend in nature, go to the gym, swim in the pool, write 500-1000 lines of code in the application for clients who will never be used by anyone or the customer’s relatives or relatives. You can get to the Silicon Valley for the last money, watch a couple of TV shows, meet a lot of people at one or two conferences, poke in the documentation of the Assembler.

And what if I say that all this is complete nonsense? What if I tell you a secret: everything is not so simple. Time is a relative thing, you can measure it with different values ​​and coefficients; My favorite criterion is the ratio of time to benefit. What if I tell you that a programmer can launch a new product on the market in 4 days? Intrigued? - Read on.

Motivation


“Our own ivangai” was recently added to the Telegram community of programmers in our community and started wildly littering left and right with voice messages (who knows, all of a sudden it’s just hard for a guy to write with his fingers on the keyboard). Naturally, people are tired of listening to two or three hours a day messages to get into the essence of the conversation - and then someone suggested creating a bot that would automatically translate all the voices into text. This is exactly the moment when the eyes light up.

Voice to text translation


I did not know how long it would take me to develop such a bot, but I already had enough experience behind them with bots from our warm, cozy chat and an open source freelance exchange in Telegram. Without hesitation, I sat down for development - the stage of connecting the Telegam Bot API modules went like clockwork. What's next? Speech recognition module - google “voice recognition api” and get among the first links a list of the best services. At the head is the Google Speech API in beta - and take it.

We receive all voice messages by a bot and somehow we direct towards Google. But, here's a bad luck: almost all npm modules of work with this service are either outdated or do not work. We try to use the built-in API under Node.js, written by Google experts - it works. What is authentication? Having tried a lot of options from the documentation from Google - which, incidentally, is also 50 to 50 is outdated or does not work - and here it is, the keys came up.

By the way, here is the code I used:

Push me!
const Speech = require('@google-cloud/speech'); const speech = new Speech({ projectId: 'voicy-151205', credentials: require('path/to/certificate/file.json') }); speech.startRecognition(filepath, { 'encoding': 'LINEAR16', 'sampleRate': 16000, 'languageCode': 'en-US', }) .then((results) => { const operation = results[0]; return operation.promise(); }) .then((transcription) => { console.log(transcription[0]); }) 

Google Audio Formats and Limitations


What is there to send voices to Google? Yeah, you need to get audio files from Telegram and send them to the Speech API servers - we try, it does not work. What's the matter? The .oga format, usual for Telegram, is not accepted - you need to decode it. What do we have to convert media? Of course, ffmpeg, known since early childhood (thanks to my dad, who once made me understand him a long time ago). How to connect it to the node? Oops! There is an npm module sharpened specifically for this. In which format do you need to convert .oga files? It turned out, Google accepts .flac - we convert, try, everything is accepted, Google responds with text, success.

But it was not there! Google does not translate files into text longer than 60 seconds unless you upload them to the Google Cloud Storage service. What is it? Immediately try the module, written by Google programmers, do not even look in the direction of outdated npm ready-made solutions. Great, files are uploaded, processed by the service, text is returned. By the way, Google has a strange concept of time - audio files 30 seconds long, for some reason, are defined as 60 seconds. It's okay - try a file longer than 30 seconds and step on the next rake - Google accepts long files only in the “LINEAR16” encoding.

What is LINEAR16? We are looking for in the ffmpeg documentation - there is nothing like that. Great, how so? We work on. We are looking for what this format or codec is for the beast - it turns out that this is “16 bit signed little endian” data. It's good that I read a couple of books on Computer Science and I know what “16 bit” is and why it is “little endian”. We are looking for what this format represents in ffmpeg - aha: “s16le”! We try to convert - it turns out, Google accepts and responds with the text, “Mission accomplished”.

Caution: below is the code that helped me with the conversion (I need to preinstall ffmpeg on the machine)!

Push me!
 const ffmpeg = require('fluent-ffmpeg'); const temp = require('temp'); ffmpeg.ffprobe(filepath, (err, info) => { const fileSize = info.format.duration; const output = temp.path({ suffix: '.flac' }); ffmpeg() .on('end', () => console.log(output)) .input(filepath) .setStartTime(0) .duration(fileSize) .output(output) .audioFrequency(16000) .toFormat('s16le') .run(); }); 

Monetization


But what is it? What are $ 2 for using Speech API? Are they all there in Google collapsed with oak? How did we use more than two hours to convert voice to text? It won't work at all - they will add my bot to 100-1000 chats, and then what should I do? From breakfast to save on the support of the bot will not work - not the scale. You need to somehow screw up the payment. Let there be 600 free seconds for each chat, and then we will ask for coverage of the cost of the Google Speech API.

How to tie payment to bot in Telegram? Clear instructions, as well as monetization tools, have not yet been delivered to Telegram - you need to somehow resolve the issue outside. Which payment service to use? So-so-so, I recently read about the guy who became the youngest billionaire - he created his own payment service. We google, we see - Stripe, and we use it. What is it with us? They have a convenient Checkout - but of course we will not use the standard form, we will make our own.

Frontend


Where to look to make interactive pages? I am an iOS programmer, I recently plunged into server development — so my feet were slightly rinsed — and then the frontend arrived, what kind of attack? We google quickly what technologies are used to create interactive sites. Angular 2, jQuery, Vanila.js - jQuery looks the simplest, and we take it - all the more, I had fun with it once upon a time. We google YouTube tutorials on jQuery - one treshak for 2-3 hours, we will understand along the way, tutorials and responses to Stack Overflow.

We quickly draw the structure of the site - a logo, a form, a couple of lines of text and a button. How to do it? I heard something about Bootstrap. We google, we go through a couple of lessons on Bootstrap 3 - we also google, we fasten a picture, a form, a button - it seems like, even adaptively it turned out. We change the site's background from white to something more creative (slightly grayish), and that site is ready.

JQuery time! We decide to make payment in a separate project - we launch “express payments” on the command line, we get the project. Great, where do I get scripts? Like, right on the site? Something is wrong - indeed, you can put them in a separate file, and we will do this. We fasten error handling to a separate label, check the Stripe form (well, they give all the necessary user interface in the Checkout module), we fasten our new project to the same database, check the payment - it passes, everything works. Along the way, of course, due to the lack of familiar front-end developers of tools, myriad times we reload the web page manually.

Here are the files that I got (carefully, not very clean code) with my small comments:

Push me!
index.hjs
 <!DOCTYPE html> <html> <head> <title>Voicy payments</title> <!-- Bootstrap, jQuery,    global.js,    style.css  Stripe--> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> <script src="/javascripts/global.js"></script> <script src="https://checkout.stripe.com/checkout.js"></script> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <!-- -,   id ,     --> <img src="/images/logo.png" alt="Voicy" class="center-block"> <h1 class="center-block text-center">Chat ID:</h1> <h1 class="center-block text-center">{{ chatId }}</h1> <p class="text-center">{{ seconds }} seconds are left in this chat.</p> <p class="text-center">You can buy more seconds below.</p> <p class="text-center"><b>$0.4 per 200 seconds</b></p> <form> <div class="center"> <!--  —  ,    --> <form class="form-inline" id="buy"> <div class="form-group"> <!--,      ,     chatId   --> <input type="hidden" name="chatId" value="{{ chatId }}"> <!-- ,   ,    --> <input type="number" class="form-control" name="numberOfSeconds" placeholder="Enter number of seconds"> <!--  — info, error  success,       global.js--> <small id="infoLabel" class="form-text text-info"></small> <small id="errorLabel" class="form-text text-danger"></small> <small id="successLabel" class="form-text text-success"></small> </div> <button type="submit" class="btn btn-primary center-block" id="buyButton">Buy</button> </form> </form> </form> </body> </html> 

global.js
 //    $(document).ready(function() { //   —  ,    var chatId; var amount; //    Stripe Checkout var handler = StripeCheckout.configure({ key: '***', image: 'https://pay.voicybot.com/images/stripe.png', locale: 'auto', // alipay: true, // ,  - AliPay // bitcoin: true, //     —     US   closed: function() { //  ,  info,   stripe $("#successLabel").empty(); $("#errorLabel").empty(); }, token: function(token) { //    ,   token        //    (,   —     ) $("#infoLabel").empty(); $("#successLabel").empty(); $("#errorLabel").empty(); //  ,    $("#infoLabel").append('Processing payment on Voicy servers...'); //   ,    index.js $.ajax({ type: 'POST', url: 'buy', data: { 'token': token.id, 'chatId': chatId, 'amount': amount }, dataType: 'json', encode: true }) .done(function(data) { //  :   —   "";  —   if (data['error']) { $("#infoLabel").empty(); $("#successLabel").empty(); $("#errorLabel").empty(); $("#errorLabel").append(data['error']); } else { $("#infoLabel").empty(); $("#successLabel").empty(); $("#errorLabel").empty(); $("#successLabel").append('Thank you for the payment!'); } }); } }); //  Stripe,    ,    window.addEventListener('popstate', function() { $("#infoLabel").empty(); $("#successLabel").empty(); $("#errorLabel").empty(); handler.close(); }); //   —      "Buy" $('form').submit(function(event) { event.preventDefault(); //      var seconds = $('input[name=numberOfSeconds]').val(); //      chatId = $('input[name=chatId]').val(); //  chat id    //    —     200 ,   if (!seconds || seconds < 200) { $("#infoLabel").empty(); $("#successLabel").empty(); $("#errorLabel").empty(); $("#errorLabel").append('Please purchase at least 200 seconds'); } else { //    —    ... var purch = seconds * 0.002 * 100; amount = seconds; $("#infoLabel").empty(); $("#successLabel").empty(); $("#errorLabel").empty(); $("#infoLabel").append('Please pay at Stripe Checkout'); // ...    Stripe handler.open({ name: 'Voicy Bot', description: 'Purchasing ' + seconds + ' seconds', currency: 'USD', amount: purch, // alipay: true, // bitcoin: true }); } }); }); 

index.js
 // Express router     HTTP   jQuery const express = require('express'); const router = express.Router(); // db —  ,          const db = require('../helpers/db'); // Stripe —       const stripe = require("stripe")("***"); /**   */ router.post('/buy', (req, res, next) => { //  token  stripe,      const token = req.body.token; //  id ,     const chatId = parseInt(req.body.chatId); // ,    const amount = parseInt(req.body.amount); var charge = stripe.charges.create({ amount: amount * 0.002 * 100, //     source: token, currency: "USD", description: "Buying seconds for Voicy" }, (err, charge) => { if (err) { res.send({ error: err.message }); //     jQuery } else { db.findChat(chatId) //     id .then((chat) => { // ,   "" chat.seconds = parseInt(chat.seconds) + amount; //     return chat.save() //   .then((newChat) => { res.send({ success: true }); //  ""   jQuery }); }) .catch((err) => { res.send({ error: err.message }); //     jQuery }) } }); }); /*    */ router.get('/:id', (req, res, next) => { const chatId = parseInt(req.params.id); // id ,     db.findChat(chatId) //    id .then((chat) => { //  ?   404! if (!chat) { const err = new Error(); err.status = 404; err.message = 'No chat found'; throw err; } // ?    ! return chat; }) .then((chat) => { //   ,       id  res.render('index', { chatId: chat.id, seconds: chat.seconds, }); }) .catch(err => next(err)); //      (     ) }); 

Main website


Any good project needs a good website - but I don’t really want to spend money on hosting. We buy a domain name and send it directly to GitHub Pages, the benefit, with this experience is enough from previous open source projects. We take the standard template, fill it with the necessary data, slightly modify the index.html - that's it.

Nginx and SSL


But how will users trust me if the form of payment is not available via https? Do not worry - swam, we know! We start CertBot on the server, we receive the necessary SSL certificates. So far, the application is available on pay. * Domain * .com: 3000 - it's useless to send users here. We configure Nginx to redirect all requests from http to https and hang the proxy from port 80 to 3000 - check, go to the site, work.

If anyone is interested, here is the nginx configuration file:

Push me!
 # HTTP -     HTTPS: server { listen 80; listen [::]:80 default_server ipv6only=on; return 301 https://$host$request_uri; } # HTTPS -      Node.js : server { listen 443; server_name your_domain_name; ssl on; #    Lets Encrypt: ssl_certificate /etc/letsencrypt/live/pay.voicybot.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/pay.voicybot.com/privkey.pem; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers '***'; #     localhost:3001: location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-NginX-Proxy true; proxy_pass http://localhost:3000/; proxy_ssl_session_reuse off; proxy_set_header Host $http_host; proxy_cache_bypass $http_upgrade; proxy_redirect off; } } 

Conclusion


My story ended abruptly, as everything turned out to be ready: one server receives all voice messages and translates them into text, the second is responsible for safely paying for the processing time of the voices, and GitHub distributes the project’s main website. The main thing that I wanted to describe in this article is that the process of creating a product, although laborious, can fit in 40-80 hours thanks to the already existing tools on the Internet.

Let me remind you that this small project includes:


If you still think that you do not have enough skills to create your own product - look around, everything has already been done for you. Unlike the same 2000, today the creation of a product does not require a deep knowledge of algorithms and data structures. What are you waiting for?

Thanks


Thank you so much for reading to the end! I will be happy to answer all comments on the article. I do not attach the link to the bot itself (although it is fully working), since this is against the rules of Habrahabr.

Rock on.

Source: https://habr.com/ru/post/316824/


All Articles