2

I'm using Telegram bot API and AWS S3 to read data from a bucket. I need to use the data from the s3 method in the Telgraf method, but I don't know how:

'use strict'

const Telegraf = require('telegraf');
const bot = new Telegraf('TOKEN')

var AWS = require('aws-sdk')
var s3 = new AWS.S3({
    accessKeyId: 'key',
    secretAccessKey: 'secret'
}) 

var params = {Bucket: 'myBucket', Key:"ipsum.txt"};

var s3Promise = s3.getObject(params, function(err, data) {
    if (err) console.log(err, err.stack);
    else              
    var words= data.Body.toString(); //WHAT I WANT IN IN COMMAND METHOD
    console.log('\n' + words+ '\n') //Returns ipsum.txt as string on console
})

bot.command('s', (ctx) => { //Bot Command
    s3Promise; //Returns ipsum.txt as string on console
    ctx.reply('Check console') //Meesage in Telegram
    //ctx.reply(<I WANT data.Body.toSting() HERE>)
});

const { PORT = 3000 } = process.env
bot.startWebhook('/', null, PORT)

How do I use the data from the s3.getObject method in ctx.reply() ?

3 Answers3

1

If you want to send the file as an attachment, you have to use: ctx.replyWithDocument. Aside from that your problem is: How do I return the response from an asynchronous call?

In this particular case you can use s3.getObject(params).promise() in order to avoid the callback API, and use it easily inside your bot.command listener.

Using async/await (Node >= 7.6) your code can be written like this

'use strict';

const Telegraf = require('telegraf');
const bot = new Telegraf('TOKEN');

const AWS = require('aws-sdk');
const s3 = new AWS.S3({
    accessKeyId: 'key',
    secretAccessKey: 'secret'
});

const params = {
    Bucket: 'myBucket',
    Key: 'ipsum.txt'
};

bot.command('s', async ctx => { // Bot Command

    try {

        // If you're sending always the same file and it won't change
        // too much, you can cache it to avoid the external call everytime
        const data = await s3.getObject(params).promise();

        ctx.reply('Check console'); // Message in Telegram

        // This will send the file as an attachment
        ctx.replyWithDocument({
            source: data.Body,
            filename: params.Key
        });

        // or just as text
        ctx.reply(data.Body.toString());

    } catch(e) {
        // S3 failed
        ctx.reply('Oops');
        console.log(e);
    }
});

const {
    PORT = 3000
} = process.env;

bot.startWebhook('/', null, PORT);

More info on how to work with files can be found on telegraf docs

PS: I tested the code and it it's fully working:

enter image description here

Marcos Casagrande
  • 37,983
  • 8
  • 84
  • 98
  • ctx.reply(data.Body.toString()); works fine, but replyWithDocument() doesn't. Might it be because [source:](https://telegraf.js.org/#/?id=senddocument) is to indicate the path and you're putting the buffer? I'm getting **throw er; // Unhandled 'error' event Error: write after end** in console. In management console I see my lambda function is using Node.js 8.10. –  May 05 '18 at 18:42
  • `source` takes a Buffer, a file path, Url, Buffer or ReadStream. Check the docs: https://telegraf.js.org/#/?id=working-with-files. Can you post the full stack trace? A pastebin would be fine. – Marcos Casagrande May 05 '18 at 18:44
  • I'm testing the code locally, and `replyWithDocument` works perfectly. Try it yourself. You can use `startPolling` instead of `startWebook` for testing locally. – Marcos Casagrande May 05 '18 at 18:51
  • I tried many times before. Any way I try to make it work by deploying using [apex up](https://up.docs.apex.sh/) I get [this error](https://pastebin.com/EWxn9s9p). Your code works locally, but I get the same error when I deploy it. Also, I covered the IP's, I'm a hobbyist and I don't know if I should leave it in the pastebin. –  May 11 '18 at 23:25
  • The issue has nothing to do with the current question. You should make a new question, so other people can help you out. I will look at it after you make the new question. – Marcos Casagrande May 11 '18 at 23:45
  • [Here it is.](https://stackoverflow.com/questions/50301677/s3-bucket-error-when-deploying-telegram-bot-using-apex-up) –  May 12 '18 at 00:03
0

While I haven't used S3, I do know that AWS services added support for Promises to their implementations to avoid using callbacks. Personally, I much prefer the use of promises as I think they lead to more readable code.

I think the following should handle the issue you're having.

'use strict'

const Telegraf = require('telegraf');
const bot = new Telegraf('TOKEN')

var AWS = require('aws-sdk')
var s3 = new AWS.S3({
    accessKeyId: 'key',
    secretAccessKey: 'secret'
}) 

var params = {Bucket: 'myBucket', Key:"ipsum.txt"};

bot.command('s', (ctx) => {
  s3.getObject(params).promise()
  .then(data => {
    ctx.reply('Check console');
    ctx.reply(data.Body.toString());
  }, err => console.log(err, err.stack));
})

const { PORT = 3000 } = process.env
bot.startWebhook('/', null, PORT)
CodyKnapp
  • 109
  • 5
  • That didn't work. I don't see any error in console, but nothing was happening. –  May 05 '18 at 15:52
  • I'm not familiar with Telegraf, so I'm guessing the problem is related to not invoking its command properly. My apologies. It certainly looks like Marcos was on the same track, but knows more about Telegraf and how to do the exact thing you're trying to do. – CodyKnapp May 06 '18 at 02:01
0

As suggested by Luca, I called bot.command inside of s3.getObject and it works!

s3.getObject(params, function(err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else              
        bot.command('s', (ctx) => {
            ctx.reply('Succesfully read from S3:\n\n' + data.Body.toString())
        });
    })