Telegram Bot -UnhandledPromiseRejectionWarning: Error: 400: Bad Request: chat not found - telegram

I am trying to get chat from one user using my telegram bot. Below is my code so far
const { Telegraf } = require('telegraf')
const bot = new Telegraf(process.env.BOT_TOKEN);
bot.telegram.getChat('username', async (ctx) =>{'incoming message ', console.log(ctx.message.tesx)})
from Doument
(method) Telegram.getChat(chatId: string | number): Promise<ChatFromGetChat>
Get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.).
#param chatId — Unique identifier for the target chat or username of the target supergroup or channel (in the format
#channelusername — )
so in my parameter i use the username of that particular chat (not group chat, only 1 person) but it return chat not found.
Any advice is appreciated

You have to pass the chat and chat id for the getChat instead of just passing a string.
bot.telegram.getChat(ctx.message.chat.id, ctx.message.chat, async (ctx) =>
{'incoming message ', console.log(ctx.message.tesx)})

Related

How do I mark a message as read?

How do I mark a message as read?
app = Client(session_name, api_id, api_hash)
#app.on_message()
async def my_handler(client, message):
await app.send_message(message.from_user.username, "ok boss")
await app.read_chat_history(message.from_user.username)
app.run()
I expected the bot's message to be ticked that he had read it
Client.read_chat_history()
Mark a chat’s message history as read.
Usable by
[X] Users
[ ] Bots
Parameters:
chat_id (int | str) – Unique identifier (int) or username (str) of the target chat. For your personal cloud (Saved Messages) you can simply use “me” or “self”. For a contact that exists in your Telegram address book you can use his phone number (str).
max_id (int, optional) – The id of the last message you want to mark as read; all the messages before this one will be marked as read as well. Defaults to 0 (mark every unread message as read).
Returns:
bool - On success, True is returned.
EXAMPLE
# Mark the whole chat as read
await app.read_chat_history(chat_id)
# Mark messages as read only up to the given message id
await app.read_chat_history(chat_id, 12345)
I expected the bot's message to be ticked that he had read it
This is not possible, messages send by a Telegram Bot will never get those checkmarks.
Those marks are only for messages send from a user-account

Generate undo email change link in firebase cloud functions

How can I generate a link to undo the email change in firebase cloud functions?
So when a user changes their email address, I want to generate a link to include in an automated email for them to click to undo this email change. Firebase sends an email when an email changes, but I want to be able to do that myself with my own code.
Currently, I can find that there are ways you can generate a link to change the user password, verify email, etc. However, I cannot find a method that I can use to generate a link to undo email change when the user changes their email.
When a user changes the email, you can store a document in Firestore containing their old email, a token and some metadata if you need to. That being said, you should update user's email from a Cloud function or your server using the Admin SDK only as there are no triggers on E-Mail change.
import jwt from "jsonwebtoken"
import {v4} from "uuid"
exports.changeEmail = functions.https.onCall(async (data, context) => {
const {newEmail} = data;
const {uid} = context.auth;
// change user's email
// send an email to verify new email is required
// generate a JWT
const token = jwt.sign({ uid, eventId: v4() }, 'jwt_signing_secret', { expiresIn: '24h' });
// add a document in Firestore containing details about this event
await admin.firestore().collection("emailChanges").doc(eventId).set({
uid, changedAt: Date.now()
})
const undoURL = `https://[YOUR_DOMAIN]/revert-email-change?token=${token}`
// E-Mail this URL to user
// Terminate this function
})
Replace [YOUR_DOMAIN] will the URL of your website. Once the user visits /revert-change-email email page of your website, call another function that verifies this token.
exports.revertEmailChange = functions.https.onCall((data, context) => {
// pass the token from frontend by checking URL params
const {token} = data
// Verify the token
const decoded = jwt.verify(token, 'jwt_signing_secret');
console.log(decoded)
const {uid, eventId} = decoded
// token is valid
// read the Firestore document using stateId and check old email
const snap = await admin.firestore().collection("emailChanges").doc(eventId).get()
if (!snap.exists) return {error: "Invalid Token"}
const {email} = snap.data()
// use updateUser() method to change email back
// delete that document from Firestore
return {data: "Email changed back successfully"}
});
You can change the lifespan of JWT token i.e. how long the URL should be valid. You can read more about JWT at jwt.io. The additional eventId token is just to prevent that JWT token so it cannot be reused.
When writing Cloud Functions for Firebase, one uses the Admin Node.js SDK.
AFAIK it is not possible, with this Admin SDK, to generate an email action link to undo an email change, as we can we can do, for example, for email verification with the generateEmailVerificationLink() method.
You will need to build your own mechanism yourself. You'll probably have to save somewhere (e.g. in Firestore) the previous email and expose an HTTP endpoint to trigger the action (HTTPS Cloud Function? Call to the Firestore REST API?). In any case you'll have to check the identity of the calling user (by either checking the Firebase ID token as a Bearer token in the Authorization header of the HTTP request or via a dedicated Firestore Security Rule).
There isn't enough details in your question to understand the exact flow of your complete use case (i.e. from the request to change email up to the action of undoing an effective change) and propose a sensible approach.

ASAuthorizationAppleIDRequest with name and mail scope returns nil values

I'm implementing Sign in with Apple and noticed that the email and fullName properties of the returned ASAuthorizationAppleIDCredential are only filled on the very first Sign-In for this Apple ID. On all subsequent Sign-Ins those properties are nil.
Is this a bug on iOS 13 or expected behaviour?
Here is the code I'm using to start the request:
#available(iOS 13.0, *)
dynamic private func signInWithAppleClicked() {
let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedScopes = [.fullName, .email]
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
}
I'm receiving the credential in this delegate method:
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else { return }
let userIdentifier = credential.user
let token = credential.identityToken
let authCode = credential.authorizationCode
let realUserStatus = credential.realUserStatus
let mail = credential.email // nil
let name = credential.fullName // nil
}
Seems like a bug but after reading different posts on apple forums it looks like this seems to be the expected behaviour.
So some takeaways.
On the first time sign in with apple (signup) make sure to create user account on your backend.
In case of any connection error with your servers, make sure you save the user details locally (because you are not getting this next time) and keep retrying to create account on your backend.
For testing on device you can revoke your apple ID login for your app. After revoking it will work like a signup next time and you will get the details like (email, name, etc).
To revoke access on your device with IOS 13.
iPhone Settings > Apple Id > Password & Security > Apple ID logins > {YOUR APP} > Stop using Apple ID
In case you're wondering how to retrieve email second and subsequent times, here's a hint: use identityToken which contains encoded in JWT user authorisation data including email.
Import this library to decode JWT: https://github.com/auth0/JWTDecode.swift
try this code
import JWTDecode
// ...
if let identityTokenData = appleIDCredential.identityToken,
let identityTokenString = String(data: identityTokenData, encoding: .utf8) {
print("Identity Token \(identityTokenString)")
do {
let jwt = try decode(jwt: identityTokenString)
let decodedBody = jwt.body as Dictionary<String, Any>
print(decodedBody)
print("Decoded email: "+(decodedBody["email"] as? String ?? "n/a") )
} catch {
print("decoding failed")
}
Or decode it at PHP backend like this:
print_r(json_decode(base64_decode(str_replace('_', '/', str_replace('-','+',explode('.', $identityTokenString)[1])))));
It is a correct behavior when implementing SignIn with Apple.
This behaves correctly, user info is only sent in the
ASAuthorizationAppleIDCredential upon initial user sign up. Subsequent
logins to your app using Sign In with Apple with the same account do
not share any user info and will only return a user identifier in the
ASAuthorizationAppleIDCredential. It is recommended that you securely
cache the initial ASAuthorizationAppleIDCredential containing the user
info until you can validate that an account has successfully been
created on your server.
To overcome this issue we can store all the required information in Keychain. I have created Singleton class for SignIn With Apple. I am sure it will help you.
Git source: https://github.com/IMHitesh/HSAppleSignIn
You need to follow below steps:
Step:1
Add the AppleSignIn folder into your project.
Step:2
Enable SignIn with Apple in Capabilities.
Step:3 -> IMPORTANT
Goto your UIViewController and Add Container view for SignIn with
apple.
if #available(iOS 13.0, *) {
appleSignIn.loginWithApple(view:viewAppleButton, completionBlock: { (userInfo, message) in
if let userInfo = userInfo{
print(userInfo.email)
print(userInfo.userid)
print(userInfo.firstName)
print(userInfo.lastName)
print(userInfo.fullName)
}else if let message = message{
print("Error Message: \(message)")
}else{
print("Unexpected error!")
}
})
}else{
viewAppleButton.isHidden = true
}
This seems to be the expected behaviour:
This behaves correctly, user info is only sent in the
ASAuthorizationAppleIDCredential upon initial user sign up. Subsequent
logins to your app using Sign In with Apple with the same account do
not share any user info and will only return a user identifier in the
ASAuthorizationAppleIDCredential. It is recommened that you securely
cache the initial ASAuthorizationAppleIDCredential containing the user
info until you can validate that an account has succesfully been
created on your server.
Source https://forums.developer.apple.com/thread/121496#379297
This is not bug but it will indicate that your authentication successfully store to your device setting.
if you want to that all information again then you need to following this states.
go to your device -> Settings -> Apple ID -> Password & Security
-> Apps Using your Apple ID -> you get list of apps used sign in with apple {find your app} -> swift left of your apps row {show Delete option} -> click on Delete
restart your app or repress sign in with apple button
Now you can get all information
In https://developer.apple.com/documentation/signinwithapplerestapi/authenticating_users_with_sign_in_with_apple it says:
Because the user’s information isn’t shared with your app in any subsequent API calls, your app should store it locally, immediately after you receive it from the API response. In case of subsequent failures in your process or network, you can read the information from local storage and try processing it again.
The email would be given on the first time sign in. If the user do not "revoke" the apple sign in of your app (which is in the user's Apple ID of system setting page) the callback for signing in would be returned with a nil email value. You could save the user id and email info of the first time sign-in successful result, and when the next time sign in to judge the difference between the return and the saved info.
A better practice is to judge the the value of ASAuthorizationAppleIDProvider.getCredentialState while your app is being "active" for syncing the sign-in state with back-end server in time.
Please refer to: How to Sign Out of Apple After Being Authenticated
I wrote a Helper class specific for this issue. This Helper class can help to save and retrieve the user info securely to and from keyChain.
I am using SwiftKeychainWrapper library to do the heavy task for me. Feel free to copy paste the helper class in your code.You might need to add any other extra information depending on your need.
import Foundation
import SwiftKeychainWrapper
/// A Helper class which abstract Keychain API related calls.
final class KeyChainService {
// MARK: - Properties
static let shared = KeyChainService()
/// Returns previous saved user name if available.
var appleUserName: String? {
return KeychainWrapper
.standard
.string(forKey: .appAppleUserName)
}
/// Returns previous saved user appleId/email if available.
var appleUserEmail: String? {
return KeychainWrapper
.standard
.string(forKey: .appAppleEmailId)
}
/// Saves the apple user name into keychain.
/// - Parameter name: Apple user name retrieved form AppleLogin.
/// - Returns: true if succeed otherwise false.
#discardableResult
func saveAppleUserName(name: String?) -> Bool {
guard let name = name else {return false}
return KeychainWrapper.standard.set(
name,
forKey: KeychainWrapper.Key.appAppleUserName.rawValue
)
}
/// Saves the apple user email into keychain.
/// - Parameter email: Apple userId/email retrieved form AppleLogin.
/// - Returns: true if succeed otherwise false.
#discardableResult
func saveAppleEmail(email: String?) -> Bool {
guard let email = email else {return false}
return KeychainWrapper.standard.set(
email,
forKey: KeychainWrapper.Key.appAppleEmailId.rawValue
)
}
/// Deletes both apple user name and saved Id from keyChain.
func deleteSavedAppleUserInfo(){
KeychainWrapper.standard.remove(forKey: .appAppleUserName)
KeychainWrapper.standard.remove(forKey: .appAppleEmailId)
}
}
// MARK: - KeychainWrapper + Extensions
extension KeychainWrapper.Key {
/// A random string used to identify saved user apple name from keychain.
static let appAppleUserName: KeychainWrapper.Key = "appAppleUserName"
/// A random string used to identify saved user apple email /Id from keychain.
static let appAppleEmailId:KeychainWrapper.Key = "appAppleEmailId"
}

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.

Messenger bot with checkbox plugin how to keep track of the conversation with user ref and user id

We'd like to add the facebook messenger checkbox plugin at the end of a request form so users can opt-in for notifications via messenger.
When the user opts in, our webhook receives a callback with the user_ref that we set in the form.
We send a confirmation of opt-in to this user_ref
But other messages we receive like delivery, read receipt or actual messages from the user do contain the user ref anymore but the user id.
This is the official documentation of facebook:
After you receive the callback event, you can call the Send API to start messaging the user using the user_ref identifier in recipient as shown below. Note that this field is the same as the unique user_ref param used before when the plugin was rendered and in confirming the opt-in.
If the call to the Send API is successful, the response will contain a recipient_id parameter, which is a stable user ID that you can now use in future API calls.
Therefore it's impossible to keep track between the initial message and new ones. Does anyone found a solution for this?
Thank you very much in advance.
You can, for example, send additional information when the user opts in using the optional ref parameter. You can send the username of the user logged on my website:
function confirmOptIn() {
FB.AppEvents.logEvent('MessengerCheckboxUserConfirmation', null, {
'app_id':'APP_ID',
'page_id':'PAGE_ID',
'ref': 'myuser#mywebsite.com',
'user_ref':'UNIQUE_REF_PARAM'
});
You will receive the username within optin in your webhook event:
{
"recipient":{
"id":"PAGE_ID"
},
"timestamp":1234567890,
"optin":{
"ref":"myuser#mywebsite.com",
"user_ref":"UNIQUE_REF_PARAM"
}
}
Then, you can call the Send API to start messaging the user using the user_ref.
If the call to the Send API is successful, the response will contain a recipient_id parameter, which is a stable user ID that you can now use in future API calls.
...so you will received the Messenger ID which you can map to the username of your website you already have. Here, I modified a little the example from the official developers site to call the send API with user_ref and map the user ID I get in the response to the username of my website:
function callSendAPICheckbox(messageData, userApplicationId) {
((userApplicationId) => {
request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: {
access_token: PAGE_ACCESS_TOKEN
},
method: 'POST',
json: messageData
},
function(error, response, body) {
if (!error && response.statusCode == 200) {
var recipientId = body.recipient_id;
var messageId = body.message_id;
if (messageId) {
console.log("Map messenger user ID %s with the username of my website %s", recipientId, userApplicationId);
} else {
console.log("Successfully called Send API for recipient %s",
recipientId);
}
} else {
console.error("Failed calling Send API for userId " +
recipientId, response.statusCode, response.statusMessage, body.error);
}
});
})(userApplicationId)
}
Why don't you make use of metadata field of sendTextMessage. Each and every message you send to your user, you send the metadata too, and when you receive response of the message being delivered, you find the metadata field in it.
Here is what I do:
When user select the checkbox plugin and event is triggered I receive the call on my server, check if it contains user_ref. If it does, then I send a text message to user with a custom metadata using user_ref. When user receives the message, the webhook send me a json data as mentioned in the documentation. To identify for which user_ref I have received this response, I set custom metadata which is combination of some string + user_ref before sending the message to user using user_ref. Using this custom metadata I identify the sender.id of the user for which I previously sent message using user_ref. The sender.id is my pageid and recipient.id the the user id which you are trying to get and using which we generally send message to the user and is also know as psid.
Above if just the logical part mentioned which I usually do.
For detail solution along with code, I have already posted it here:

Resources