Get Reply Message from Telegram Bot API - telegram

I'm developing a Bot that I want users to call in a reply to a previous message. So you would reply to a message with the bot command.
For Example
User 1: Hello World
User 2: (Reply to Hello World) /command test message
Right now I am only able to grab the text sent directly in the command ("test message"), but not the first message ("Hello World"). According to their documentation, I should be able to get it from reply_to_message. However, all I'm seeing in my logs from the webhook is this.
event: {
body: {
update_id: 5632431,
message: {
message_id: 43,
from: {
id: < my_user_id > ,
first_name: 'User 2',
username: 'user_2_username',
language_code: 'en'
},
chat: {
id: < chat_id > ,
title: < chat_name > ,
type: 'group',
all_members_are_administrators: true
},
date: 1498342725,
text: '/command test message',
entities: [{
type: 'bot_command',
offset: 0,
length: 5
}]
}
}
}
Am I doing something wrong? Anyone have experience getting a reply message?
Any help would be appreciated.

Goto #BotFather, and turn off privacy mode
/setprivacy — Set which messages your bot will receive when added to a group. With privacy mode disabled, the bot will receive all messages.
You might need re-add your bot to group after set this.

When a bot is in privacy mode then it will only receive (copied from docs):
Messages that start with a slash ‘/’ (see Commands above)
Replies to the bot's own messages
Service messages (people added or removed from the group, etc.)
Messages from channels where it's a member
This does not include messages that were replied to.
Privacy mode is enabled by default for all bots, except bots that were added to the group as admins (bot admins always receive all messages).
So the only way to get messages being replied to is to disable privacy mode as Sean suggested.

I was dealing with the same problem recently, two things worked for me:
1 - Disable Privacy Mode and,
2 - Using #botname for mentions (no /botname), that way I was able to get reply_to_message field.

Sharing my code; try this, it should succeed.
$chatID = $this->getChatID();
$sendto = API_URL . "sendmessage?chat_id=" . $chatID . "&text=" . urlencode($msg) . "&reply_to_message_id=" . $messageID;
file_get_contents($sendto);

Related

Telegram bot sends "my_chat_member" object instead of "message" object on start via webhook

I have a telegram bot where the user can send /start command and I will receive this command on my server via web hook. In 99% of cases the request from telegram looks like this:
{
"update_id":99999999,
"message":{
"message_id":9999,
"from":{
"id":999999999,
"is_bot":false,
"first_name":"first_name",
"last_name":"last_name",
"language_code":"code"
},
"chat":{
"id":99999999,
"first_name":"first_name",
"last_name":"last_name",
"type":"private"
},
"date":1665383118,
"text":"/start",
"entities":[
{
"offset":0,
"length":6,
"type":"bot_command"
}
]
}
}
The object is "message" and I have a text "/start" there.
But sometimes from some new users who hasn't used this bot before when they send start the request from telegram look like this:
{
"update_id":999999999,
"my_chat_member":{
"chat":{
"id":999999999,
"first_name":"first_name",
"type":"private"
},
"from":{
"id":999999999,
"is_bot":false,
"first_name":"first_name",
"language_code":"code"
},
"date":1665381194,
"old_chat_member":{
"user":{
"id":8888888888,
"is_bot":true,
"first_name":"bot_name",
"username":"bot_name"
},
"status":"member"
},
"new_chat_member":{
"user":{
"id":8888888888,
"is_bot":true,
"first_name":"bot_name",
"username":"bot_name"
},
"status":"kicked",
"until_date":0
}
}
}
The object "my_chat_member" and the "start" command was not received from this user.
This happens for users on IPhone, Android, PC, Web.
I can't understand why it happens and how to fix.
The problem was on my server side. I used int32 for users' and chats' ids but they can be bigger. Using the correct type solved the problem.
Message type "my_chat_member" means somebody described as "from" wants to unsubscribe from bot. "status":"kicked" says the same. Here https://core.telegram.org/bots/api you can read Getting updates topic

Can I import OneSignal tokens to FCM?

I have several thousand OneSignal web push notification tokens I want to import to FCM. Is there a way to do this?
I see this endpoint which requires the https://fcm.googleapis.com/fcm/send/...key... endpoint that OneSignal gives me, but I don't know what to put in for auth and p256dh.
https://developers.google.com/instance-id/reference/server#create_registration_tokens_for_apns_tokens
So yes this can be done. First you will need to contact OneSignal support and get the public and private VAPID keys for your app. Each app in your dashboard will have a different set.
Next you will need to make an API call to OneSignal in order to export the users in a CSV file.
You can find the API url in the docs and use curl or use your favorite language. I used Node + Axios to make my calls. The API call will supply you with a link to download the CSV.
Here is the documentation https://documentation.onesignal.com/reference#csv-export
You want to make sure you add the "extra_fields" parameter to your request with the "web_auth" and "web_p256" fields added. The CSV will provide you with the other piece of the puzzle which is the endpoint url in their identifier column.
Once you have all this information you can now send pushes using a library such as web-push for Node
https://github.com/web-push-libs/web-push
Hope that helps!
EDIT
As Cedric stated the actual push payload is a little bit more complicated because you need to comply with the OneSignal Service worker data handling.
You can see the formatting starting at line 313 here
If you are using a library like web-push for Node to send your push payloads your payload would be formatted something like this for a standard push to a OneSignal service worker.
const uuidv1 = require('uuid/v1')
const webpush = require('web-push')
let subscription = {
endpoint: 'USER ENDPOINT URL',
keys: {
auth: 'USER AUTH KEY',
p256dh: 'USER P256 KEY'
}
}
let vapid = { private: 'VAPID PRIVATE KEY', public: 'VAPID PUBLIC KEY' }
// Format Message for OneSignal Service Worker
let notification = JSON.stringify({
custom: {
i: uuidv1(), //Generate UUID for the OneSignal Service worker to consume
u: 'CLICK URL'
},
title: 'TOP TITLE',
alert: 'MESSAGE BODY',
icon: 'ICON IMAGE URL'
})
webpush.setVapidDetails('mailto: sendError#YourEmail.com', vapid.public, vapid.private)
webpush.sendNotification(subscription, notification)
It's much more complex than Dan's answer. If your users don't subscribe to your own service worker, it won't work. OS will send its default notification when an 'unknown' error occurs, which it will send "You have new updates" as a notification to the user even though you passed different payload. You also need to pass: "custom": { "i": uuidv1() } to your payload for it to work. (don't forget to install uuid first through npm and call it). Check out this link and you'll figure out what other payload props you need to pass.

How to handle Firebase Cloud Messaging onTokenRefresh on the back end

We have a cross-platform app that uses Firebase Cloud Messaging to drive an in-app chat feature. Some users might use the app actively on more than one device. So, whenever a user's device receives an onTokenRefresh trigger, we send that new registration token to the server to be saved against the user. Now say a user already has some registration tokens stored in the server database, how will we know if those tokens were for the same device and should now be deleted or if they are for a different device and we should keep sending to all of them?
I have read the docs on Device Group Messaging, but it looks like too much overhead for our application and it doesn't look like the Firebase server will automatically delete a superseded registration token from the group for you.
If we simply assume all the user's registration tokens on record are active and send to all, can we use the response to decide if we need to prune a token on the server?
{
"multicast_id": 6538766984100364080,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"message_id": "0:1510294979553090%029da28f029da28f"
}
]
}
According to this answer and some tests against the HTTP API with replaced tokens, it doesn't look like the "success":1 result is a reliable indicator that the token should not be removed, because replaced tokens tend to live on. Also, a "success": 0 result might not be a reliable indicator that we can remove the token, because it might just indicate an ad-hoc network error on a valid, active token.
The API documentation talks about how to interpret an optional registration_id in the result, but it is not clear how this differs from a NotRegistered error and what the best action is to take.
Any insight or best practice on how to handle and manage the arrival of a FCM device token on the server will be much appreciated.
I also came across the exact challenge and had to resolve to a solution:
Storing each token for the user against the device id.
It's interesting enough to know that this function in fact exists in the firebase messaging method. But more surprising is the fact that there's no documentation to handle such scenario.
https://firebase.google.com/docs/reference/android/com/google/firebase/iid/FirebaseInstanceId.html#getId()
So in summary, while sending the new token to the server, also send along the device id returned by the getId() method and use it to enforce uniqueness of token per device.
Problem solved :D
We are going with the approach where we assume all onTokenRefresh ids are new, additional devices that we add to the device list on the server. Then, whenever we send a message we use the returned result to delete or replace deprecated device tokens. Implementation in PHP:
// $devices is a list of the device ids to send to
// 1. send a message to a list of devices
$response = Firebase::request('POST', 'send', ['json' => $this->payloadFor($devices)]);
// 2. check the response to see if we need to make changes to the device list
// if it is a network error, no changes needed
if ($response->getStatusCode() != 200) {
Log::info("FCM http error " . $response->getStatusCode());
return;
}
$body = json_decode($response->getBody(), $asArray = true);
// do we need to dig deeper?
if ($body['failure'] == 0 && $body['canonical_ids'] == 0) return;
if (count($body['results']) != count($devices)) {
Log::info("FCM error : device count not matching result count");
return;
}
// we have errors that need processing, so step through the results list
foreach ($body['results'] as $key => $result) {
if (isset($result['error'])) {
switch ($result['error']) {
case 'NotRegistered':
case 'InvalidRegistration':
$deletedRows = Device::where('token', $devices[$key])->delete();
Log::info("FCM trimmed: $devices[$key]");
break;
default:
Log::info("FCM error " . $result['error']);
break;
}
}
// we need to update some device tokens
if (isset($result['registration_id'])) {
Device::deprecate($devices[$key], $result['registration_id']);
Log::info("FCM replaced: " . $devices[$key]);
}
}

Telegram Bot How to delete or remove a message or media from a channel or group

I want to know an example of removing message or file like a photo
I did not find any functional tutorial in this regard,
There is no such functionality in Telegram Bot API right now.
UPD 2017-05-19: There is an official method deleteMessage, more info:
https://core.telegram.org/bots/api#deletemessage
https://stackoverflow.com/a/43965602/1140438
There is an official support of deleteMessage method in Bot API 3.0. More details here:
https://core.telegram.org/bots/api#deletemessage
https://api.telegram.org/botTOKEN/deleteMessage?chat_id=CID&message_id=MID
As you can see there are two arguments: chat_id and message_id.
You can remove bot's messages or other messages (if bot is admin) except service messages (such as join/leave messages).
On success, it will return following JSON object:
{"ok":true,"result":true}.
If you are trying to remove service message or other user's message, but bot is not an admin:
{"ok":false,"error_code":400,"description":"Bad Request: message can't be deleted"}.
If you are trying to remove non-existent message or its already deleted:
{"ok":false,"error_code":400,"description":"Bad Request: message to delete not found"}
Kindly check with the below code snippet!, the below code have worked for me!
String chatId = String.valueOf(callbackQuery.getMessage().getChatId());
Integer messageId = callbackQuery.getMessage().getMessageId();
DeleteMessage deleteMessage = new DeleteMessage(chatId, messageId);
try {
execute(deleteMessage);
}catch(TelegramApiException tae) {
throw new RuntimeException(tae);
}
you can forward message and save message id, and then remove that message. if you can do it, your message exist.
do it:
try:
mes=bot.forward_message(chat_id=?,from_chat_id=?,message_id=?)
bot.delete_message(chat_id=?,message_id=mes.id)
except:
print("your message deleted")
There are two methods in bot api that let you to edit a message: editMessageText and editMessageCaption. It is not ideal, but you can use it as an alternative.
For example by editing the message to:
"This message is unavailable."
Using python, if you have a CommandHandler() you can read the chat_id and message_id like so:
dispatcher.add_handler(CommandHandler("start", handler_start))
def handler_start(update: Update, context: CallbackContext):
chat_id = update.message.chat_id
message_id = update.message._id_attrs[0]
context.bot.delete_message(chat_id, message_id)
If on php. I send message. Get response from it (message id of bot) And use deleteMessage
<?php
$botToken = "yourBotToken";
$botAPI = "https://api.telegram.org/bot" . $botToken;
$update = json_decode(file_get_contents('php://input'), TRUE);
$msg = $update['message']['text'];
if ($msg == '/start') {
$data = http_build_query([
'text' => "test message (delete this)",
'chat_id' => $update['message']['chat']['id'],
]);
$send = file_get_contents($botAPI . "/sendMessage?{$data}");
$response = json_decode($send), true); // decode response
$message_id = $response['result']['message_id']; // get bots message
// Deleting message
$data_del = http_build_query([
'chat_id' => $update['message']['chat']['id'],
'message_id' => $message_id,
]);
file_get_contents($botAPI . "/deleteMessage?{$data_del}");
}
https://api.telegram.org/botTOKEN/deleteMessage?chat_id=CID&message_id=MID
Example
https://api.telegram.org/bot123456789:zzzzzzzzxxxxxxxxxxyyyyyyyyyy/deleteMessage?chat_id=123456789&message_id=123456,
It is important that the id of the message temine with a (comma) (,) and you can see it in the json when you send the message

Timeout error for AWS SNSClient Publish request

Here is the piece of code :
//Publishing the topic
snsClient.Publish(new PublishRequest
{
Subject = Constants.SNSTopicMessage,
Message = snsMessageObj.ToString(),
TopicArn = Settings.TopicArn
});
I am getting the below error :
The underlying connection was closed: A connection that was expected
to be kept alive was closed by the server.
And here is the screenshot of detailed error:
But not able to get an idea how to solve this. Any hint or link will helpful.
We had the exact same issue happen to us. We got this error about 40 times a day, which was less than 0.1% of the successful push notifications we sent out.
Our solution? Update the AWSSDK NuGet package from 1.5.30.1 to 2.3.52.0 (the latest v2 release for ease-of-upgrade). As soon as we updated, the errors stopped happening. I looked through lots of release notes and couldn't find anything specifically mentioning this issue. We have no idea why the update worked, but it did.
I hope this helps you and anyone else fix this issue.
This problem may occur when one or more of the following conditions are true:
• A network outage occurs.
• A proxy server blocks the HTTP request.
• A Domain Name System (DNS) problem occurs.
• A network authentication problem occurs.
[https://nilangshah.wordpress.com/2007/03/01/the-underlying-connection-was-closed-unable-to-connect-to-the-remote-server/]1
make sure your payloads size should not exceed more than 256 kb
make sure you had configured timeout property of the PutObjectRequest
Take a look sample aws sns request code (from https://stackoverflow.com/a/13016803/2318852)
// Create topic
string topicArn = client.CreateTopic(new CreateTopicRequest
{
Name = topicName
}).CreateTopicResult.TopicArn;
// Set display name to a friendly value
client.SetTopicAttributes(new SetTopicAttributesRequest
{
TopicArn = topicArn,
AttributeName = "DisplayName",
AttributeValue = "StackOverflow Sample Notifications"
});
// Subscribe an endpoint - in this case, an email address
client.Subscribe(new SubscribeRequest
{
TopicArn = topicArn,
Protocol = "email",
Endpoint = "sample#example.com"
});
// When using email, recipient must confirm subscription
Console.WriteLine("Please check your email and press enter when you are subscribed...");
Console.ReadLine();
// Publish message
client.Publish(new PublishRequest
{
Subject = "Test",
Message = "Testing testing 1 2 3",
TopicArn = topicArn
});
// Verify email receieved
Console.WriteLine("Please check your email and press enter when you receive the message...");
Console.ReadLine();
// Delete topic
client.DeleteTopic(new DeleteTopicRequest
{
TopicArn = topicArn
});

Resources