Telegram bot - receive photo URL - node.js

When a user send an image via Telegram bot it there any way to get the image URL? or I just need to save the image somewhere?

In the message array you receive you can find the key photo.
There you will find multiple arrays with the following format
"file_id" : "XXXX",
"file_size" : 1107,
"width" : 90,
"height" : 51
From one of those array you need to take the file_id. You can then request the file_path with a simple get get on the url https://api.telegram.org/bot<token>/getFile?file_id=<file_id>
You will receive an array that looks as following
"ok" : true,
"result" : {
"file_id" : "XXXX",
"file_size" : 27935,
"file_path" : "photo\/file_1.jpg"
}
From the result you need the file_path and you then got the image location https://api.telegram.org/file/bot<token>/<file_path>

This is a three steps process
First when the user send an image, your bot get a JSON structure like this:
Array
(
[update_id] => 820488009
[message] => Array
(
[message_id] => 11338
[from] => Array
(
[id] => xxxxxx
[is_bot] =>
[first_name] => ANSB
[language_code] => fr
)
[chat] => Array
(
[id] => 333333333
[first_name] => ANSB
[type] => private
)
[date] => 1606316785
[photo] => Array
(
[0] => Array
(
[file_id] => AgACAgEAAxkBAAIsSl--cvE_bez8g1Kzbk6LsR4JZOJWAALxqDEbw8TxRQpbG7Np1dvbARV2ShcAAwEAAwIAA20AA6SRAAIeBA
[file_unique_id] => AQADARV2ShcAA6SRAAI
[file_size] => 34888
[width] => 320
[height] => 240
)
[1] => Array
(
[file_id] => AgACAgEAAxkBAAIsSl--cvE_bez8g1Kzbk6LsR4JZOJWAALxqDEbw8TxRQpbG7Np1dvbARV2ShcAAwEAAwIAA3gAA6WRAAIeBA
[file_unique_id] => AQADARV2ShcAA6WRAAI
[file_size] => 204583
[width] => 800
[height] => 600
)
[2] => Array
(
[file_id] => AgACAgEAAxkBAAIsSl--cvE_bez8g1Kzbk6LsR4JZOJWAALxqDEbw8TxRQpbG7Np1dvbARV2ShcAAwEAAwIAA3kAA6KRAAIeBA
[file_unique_id] => AQADARV2ShcAA6KRAAI
[file_size] => 372915
[width] => 1280
[height] => 960
)
)
)
)
As you can see, Telegram create low resolution of the image. If the original image is small, you can have only the original. If it's medium you'll get two. Here yu can see I have 3 images (the original is the big one 1280*960).
So you have to check the size of the array of image wih (eg i PHP)
$nbr_image = count($jsondata['message']['photo']);
in order to read the file_id of your choice so the oe of the smallest, the biggest etc... Take care, the id is NOT the file_unique_id but the big one so the file_id.
Notice if the user send in one time more than one picture, you will get one message for each picture. So each message is about ONE picture, in multiple resolutions.
Once your bot has the file_id, you must call Telegram sending the file_id. The call is a basic one with:
https://api.telegram.org/bot<token_of_your_bot>/getFile?file_id=<file_id of the picture>
You get back a JSON with:
{"ok":true,
"result":
{"file_id":"AgACAgEAAxkBAAIsUV--hZoxZ5_ctnfbVa0zFWjRtMYUAALyqDEbw8TxRdkTI6iDNvHUmKQSMAAEAQADAgADeAADsx8EAAEeBA",
"file_unique_id":"AQADmKQSMAAEsx8EAAE",
"file_size":41597,
"file_path":"photos/file_0.jpg"
}
So a copy of the file_id, the weight (notice you don't get back pixel size!) and the path.
After that, just make a call using the path like this:
https://api.telegram.org/file/bot<token_of_your_bot>/<file_path from the JSON>
and you'll get the picture
One point: each time I get a JSON with the picture in more than one resolution, the big one is the last. But I've find nothing in the doc about that fact. So I'm note sure you can't have the big one in index [0]...

I know I'm too late, but I was researching for it too long. Here is the answer:
const TelegramBot = require('node-telegram-bot-api');
let bot = new TelegramBot(token, {polling: true});
And the function to download it requires file id, which comes within the message
var file_id = (msg.photo[msg.photo.length-1].file_id);
var downloadDir = './images';
let something = ''
var https = require('https')
bot.getFileLink(fileId).then( async (fileUri) => {
var base64Img = require('base64-img');
let time = process.hrtime();
let extension = fileUri.split('.').pop();
let newName = `${time[0]}${time[1]}.${extension}`;
let file = fs.createWriteStream(`${downloadDir}/${newName}`);
let request = await https.get(fileUri, (response) => {
response.pipe(file);
});
file.on('finish', () =>{
console.log('msg.text ='/images/'+newName')
})
//
});
};
Main function is the bot.getFileLink(fileId). Hope it will help who will read this :)

Related

Shopify Custom Carrier Service Error : Your cart has been updated and the items you added can’t be shipped to your address

I've create a shopify app using Codeigniter 4. It's a carrier service app. The carrier service is created successfully and I can see it in my development store in settings under manage rates but I'm getting error while checking out. It says - Your cart has been updated and the items you added can’t be shipped to your address. Remove the items to complete your order. Below is the code to create the carrier service:
$response = $client->post(
"carrier_services",
[
"carrier_service" => [
'active' => true,
"name" => "Faast Delivery",
"callback_url" => "https://app.faastdelivery.com/shopify/rate",
"service_discovery" => true,
]
]
);
And here is my callback function.
public function rate(){
// log the raw request -- this makes debugging much easier
$filename = time();
$input = file_get_contents('php://input');
file_put_contents($filename.'-input', $input);
// parse the request
$rates = json_decode($input, true);
// log the array format for easier interpreting
file_put_contents($filename.'-debug', print_r($rates, true));
// total up the cart quantities for simple rate calculations
$quantity = 0;
foreach($rates['rate']['items'] as $item) {
$quantity =+ $item['quantity'];
}
// use number_format because shopify api expects the price to be "25.00" instead of just "25"
// overnight shipping is 5.50 per item
$overnight_cost = number_format($quantity * 5.50, 2, '', '');
// regular shipping is 2.75 per item
$regular_cost = number_format($quantity * 2.75, 2, '', '');
// overnight shipping is 1 to 2 days after today
$on_min_date = date('Y-m-d H:i:s O', strtotime('+1 day'));
$on_max_date = date('Y-m-d H:i:s O', strtotime('+2 days'));
// regular shipping is 3 to 7 days after today
$reg_min_date = date('Y-m-d H:i:s O', strtotime('+3 days'));
$reg_max_date = date('Y-m-d H:i:s O', strtotime('+7 days'));
// build the array of line items using the prior values
$output = array('rates' => array(
array(
'service_name' => 'Faast Delivery',
'service_code' => 'FSTD',
'total_price' => $overnight_cost,
"description" => "This is the fastest option by far",
'currency' => 'EUR',
'min_delivery_date' => $on_min_date,
'max_delivery_date' => $on_max_date
),
array(
'service_name' => 'Faast Delivery',
'service_code' => 'FSTD',
'total_price' => $regular_cost,
"description" => "This is the fastest option by far",
'currency' => 'EUR',
'min_delivery_date' => $reg_min_date,
'max_delivery_date' => $reg_max_date
)
));
// encode into a json response
$json_output = json_encode($output);
// log it so we can debug the response
file_put_contents($filename.'-output', $json_output);
// send it back to shopify
header('Content-Type: application/json');
echo $json_output;
// return $json_output;
}
Thanks in advance.
I guess and from my experience that your response took more than 10 secs and for that, a message was returned instead.
please refer to the below timeout period for each request per min:
RPM under 1500: read timeout will be 10 seconds
RPM between 1500 and 3000: read timeout will be 5 seconds
RPM over 3000: read timeout will be 3 seconds
Refrence: Dynamic Timeout for CarrierService API Rate Requests

Translate simple text to json with node.js

today i'd like to translate a text to json data
exemple :
1 kokoa#0368's Discord Profile Avatar kokoa#0000 826
2 Azzky 陈东晓#7475's Discord Profile Avatar Azzky 陈东晓#0000 703
3 StarKleey 帅哥#6163's Discord Profile Avatar StarKleey 帅哥#0000 640
to =>
{
"kokoa#0000": "826",
"Azzky 陈东晓#0000": "703",
"StarKleey 帅哥#0000": "640"
}
for the moment i have this one :
fs.appendFile(`./Text/mess.txt`, `${body.replace(/s Discord Profile Avatar/g, '').split(/[\n \t ' ']/)}`, function (err) {
So the result is this one =>
1,kokoa#0368,,kokoa#0000,826
2,Azzky 陈东晓#7475,,Azzky 陈东晓#0000,703
3,StarKleey 帅哥#6163,,StarKleey 帅哥#0000,640
But i would like delete firsts numbers, delete the duplicate array
and make this one on .json
Someone know how can i do this ? And if i need new method to make this one, it's not a problem.
Thank you.
You can read file line by line and convert each line to a 2-elements array with key and value. For example, first line can be converted to:
['kokoa#0000','826']
As you can se 0 index is a key and 1 index is a value. When you convert all lines to this kind of array you can reduce it to an object and convert it to JSON.
See example method:
function convertLinesToJson(lines) {
const dpaText = "Discord Profile Avatar";
const jsonObject = lines
//only with 'Discord ...' text
.filter(line => line.includes(dpaText))
//get what is after 'Discord ...' text and split into 2-elements array
.map(line => {
const index = line.indexOf(dpaText) + dpaText.length;
const keyValue = line.substr(index).trim().split(/[ ]{4}/);
return keyValue;
})
// convert [key, value] array into an object
.reduce((accumulator, currentValue) => {
accumulator[currentValue[0]] = currentValue[1];
return accumulator;
}, {});
//convert object to JSON
return JSON.stringify(jsonObject);
}
Usage:
let lines = [
"1 kokoa#0368's Discord Profile Avatar kokoa#0000 826",
"2 Azzky 陈东晓#7475's Discord Profile Avatar Azzky 陈东晓#0000 703",
"3 StarKleey 帅哥#6163's Discord Profile Avatar StarKleey 帅哥#0000 640"
];
const json = convertLinesToJson(lines);
console.log(json);
Above example code prints:
{"kokoa#0000":"826","Azzky 陈东晓#0000":"703","StarKleey 帅哥#0000":"640"}
See also:
Read a file one line at a time in node.js?

Array list with 2 values and doing it to a top 10 list

im working on a Discord bot and have a reputation system with fs (npm package) and saving peoples reps in a file and doing the file name as they discord id
now im working on a top 10 command and would need some help here, i currently have this as code:
let users = [];
let reps = [];
fs.readdirSync('./data/reps/').forEach(obj => {
users.push(obj.replace('.json', ''))
let file = fs.readFileSync(`./data/reps/${obj}`)
let data = JSON.parse(file)
reps.push(data.reps)
})
let top = [...users, ...reps]
top.sort((a,b) => {a - b})
console.log(top)
the files form the users are like this:
{
"users": [
"437762415275278337"
],
"reps": 1
}
users are the current users that can't rep the persion anymore and don't need to use it in the command
i wan to get the top 10 of reps so that i can get the user id and how many reps they have, how could i do it with the code above?
You could try this
const topTen = fs.readdirSync('./data/reps/').map(obj => {
const file = fs.readFileSync(`./data/reps/${obj}`);
const data = JSON.parse(file);
return { ...data, name: obj.replace('.json', '') };
}).sort((a, b) => a.reps - b.reps).slice(0, 10);
console.log(topTen);
I would change how you push the data
const users = [];
fs.readdirSync('./data/reps/').forEach(obj => {
let file = fs.readFileSync(`./data/reps/${obj}`)
let data = JSON.parse(file)
reps.push({ reps: data.reps, id: obj.replace(".json", "") });
})
That way when you sort the array the id goes along with
//define this after the fs.readdirSync.forEach method
const top = users.sort((a,b)=> a.reps-b.reps).slice(0,10);
If you want an array of top ids
const topIds = top.map(e => e.id);
If you want a quick string of it:
const str = top.map(e => `${e.id}: ${e.reps}`).join("\n");
Also you should probably just have one or two json files, one would be the array of user id's and their reps and then the other could be of user id's and who they can't rep anymore

How do I cleanse the data (removing parenthesis and quotes) in NodeJS?

I have a csv sheet where some of the cells could have either single record or multiple records like an array. A sample cell would look like either ["London"] or ["Cairo", "Montreal", "Paris"] - with the parenthesis included. I'm using sever.js to show these records in a visualization on a webpage.
Everything's fine except the parenthesis and quotes are being shown in the visualization. How do I write a rule/logic in nodeJS such that both parenthesis and quotes are not shown in the final visualization?
There's no point in cleansing the data in the csv file itself because I'm gonna have to use this code for hundreds of csv files, so it's better to write a rule in nodeJS.
The following's the server.js code I've used so far:
/**
* API for getting country Names
*/
app.get('/get-country-names.json', (req, res) => {
william.william().then(response => {
res.json(response);
}).catch(message => res.send("Failed to read file" + message));
});
/**
* API to read file and return response in json
* Takes country id as parameter
*/
app.post('/get-city-sources.json', (req, res) => {
let countryName = req.body.countryName;
let city = req.body.city;
william.william().then(sources => {
let filteredSources = [{ name: city, parent: null, color: 'red' }];
Array.from(new Set(sources.filter(source => source.countryName === countryName && source.city === city).map(src => src.source)))
.forEach(source => {
let sourceArr = source.split(',');
console.log(sourceArr);
sourceArr.forEach(src => filteredSources.push({ name: src, parent: city, color: 'blue' }));
});
res.json(filteredSources);
}).catch(message => res.send("Failed to read file" + message));
});
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
Regular expression should do the trick. I'm not exactly sure where the stripping should happen within your code but this is a sample function that uses regular expression to strip out square brackets and quotes:
const formatCellString = (cell = '') => {
return cell
// strip out the square brackets
.replace(/^\[(.*?)\]$/, "$1")
// strip out the quotes
.replace(/("\b)|(\b")/g, '')
}
formatCellString('["Cairo", "Montreal", "Paris"]'); => // Cairo, Montreal, Paris
formatCellString('["London"]'); => // London

Elastic Search-Search string having spaces and special characters in it using C#

I am looking for ElasticSearch nest query which will provide exact match on string having spaces in it using C#.
for example - I want to search for a word like 'XYZ Company Solutions'. I tried querystring query but it gives me all the records irrespective of search result. Also i read on the post and found that we have to add some mappings for the field. I tried 'Not_Analyzed' analyzer on the field but still it does not worked.
Here is my code of C#
var indexDefinition = new RootObjectMapping
{
Properties = new Dictionary<PropertyNameMarker, IElasticType>(),
Name = elastic_newindexname
};
var notAnalyzedField = new StringMapping
{
Index = FieldIndexOption.NotAnalyzed
};
indexDefinition.Properties.Add("Name", notAnalyzedField);
objElasticClient.DeleteIndex(d => d.Index(elastic_newindexname));
var reindex = objElasticClient.Reindex<dynamic>(r => r.FromIndex(elastic_oldindexname).ToIndex(elastic_newindexname).Query(q => q.MatchAll()).Scroll("10s").CreateIndex(i => i.AddMapping<dynamic>(m => m.InitializeUsing(indexDefinition))));
ReindexObserver<dynamic> o = new ReindexObserver<dynamic>(onError: e => { });
reindex.Subscribe(o);**
**ISearchResponse<dynamic> ivals = objElasticClient.Search<dynamic>(s => s.Index(elastic_newindexname).AllTypes().Query(q => q.Term("Name","XYZ Company Solutions")));** //this gives 0 records
**ISearchResponse<dynamic> ivals1 = objElasticClient.Search<dynamic>(s => s.Index(elastic_newindexname).AllTypes().Query(q => q.Term(u => u.OnField("Name").Value("XYZ Company Solutions"))));** //this gives 0 records
**ISearchResponse<dynamic> ivals = objElasticClient.Search<dynamic>(s => s.Index(elastic_newindexname).AllTypes().Query(#"Name = 'XYZ Company Solutions'"));** //this gives all records having fields value starting with "XYZ"
If anyone have complete example or steps in C# then can you please share with me?
Have you tried the match_phrase query?
The query DSL the request is the following:
"query": {
"match_phrase": {
"title": "XYZ Company Solutions"
}
}
In C# try the following:
_client.Search<T>(s => s
.Index(IndexName)
.Types(typeof (T))
.Query(q => q.MatchPhrase(m => m
.OnField(f => f.Name)
.Query("XYZ Company Solutions"))));
Check the official documentation for more information:
http://www.elastic.co/guide/en/elasticsearch/guide/master/phrase-matching.html#phrase-matching
Please refer the below code ,I think this will meet your requirements.
Here I have created and mapped index with dynamic template and then did the XDCR.
Now all string fields will be not_analysed.
IIndicesOperationResponse result = null;
if (!objElasticClient.IndexExists(elastic_indexname).Exists)
{
result = objElasticClient.CreateIndex(elastic_indexname, c => c.AddMapping<dynamic>(m => m.Type("_default_").DynamicTemplates(t => t
.Add(f => f.Name("string_fields").Match("*").MatchMappingType("string").Mapping(ma => ma
.String(s => s.Index(FieldIndexOption.NotAnalyzed)))))));
}
Thanks
Mukesh Raghuwanshi
It looks like you just need to refresh the new index following the re-indexing operation.
Using your code example (and your first term query), I was seeing the same result -- 0 hits.
Adding the following Refresh call after the reindex.Subscribe() call results in a single hit being returned:
objElasticClient.Refresh(new RefreshRequest() { });

Resources