Send message to user when he is online in Pyrogram - python

I am using Pyrogram to make userbot. I need to check if user is online and if he is online I will send him a message. I couldn't understand documentation.
from pyrogram import Client
app = Client(
"my_account",
api_id= 111111111,
api_hash='bbbbbbbbbbbbbbbbb'
)
chat_id = 777777777
with app:
peer = app.resolve_peer(chat_id)
if(is_user_online(chat_id)):
app.send_message(chat_id=chat_id, text='Hello!')
I tried to use pyrogram.types.User, but I didn't understand what I am doing.

To check if a user is online, you could use the get_users() method of the Client class that checks the status field of the returned User object.
from pyrogram import Client
app = Client(
"my_account",
api_id= 111111111,
api_hash='bbbbbbbbbbbbbbbbb'
)
chat_id = 777777777
with app:
user = app.get_users(chat_id)
if str(user.status) == "UserStatus.ONLINE"
app.send_message(chat_id=chat_id, text="Hello!")
The status field of the User object tells you when the user was online for the last time.

Related

Transfer Telegram channel ownership using pyrogram library

How to transfer channel ownership?
I want to transfer ownership of a Telegram channel using pyrogram.
There is a promote_chat_member method but it does not have an owner input parameter. There is also a pyrogram.raw.functions.channels.EditCreator method but I don't understand how to use it.
Try this in order to use the pyrogram.raw.functions.channels.EditCreator method
NOTES:
This operation must be done with an user account. It's necessary that you pass your phone number to the Client. (This operations can't be performed by bots)
The user must be a member of the channel. The code will promote the
user to Admin.
Get new_owner_id and channel_id using IDBot in Telegram (#username_to_id_bot). You'll need to send the username of the new owner and the join link of the channel to the bot.
Set a 2FA password in your account at least 1 week before running the
code (it's an API requirement)
Requirements
pip install pyrogram
pip install nest-asyncio
CODE
from pyrogram import Client
from pyrogram.raw.functions.channels import EditCreator
from pyrogram.raw.functions.account import GetPassword
import nest_asyncio
nest_asyncio.apply()
app = Client("app_name_you_like",api_id="your_api_id",api_hash="your_api_hash",phone_number="your_phone_number")
channel_id = channel_id #int -> enter your channel_id here
user_id = new_owner_id #int -> enter your new_owner_id here
async def make_owner():
await app.start()
channel_data = await app.resolve_peer(channel_id)
user_data = await app.resolve_peer(user_id)
password = await app.invoke(GetPassword())
#Make user Admin
await app.promote_chat_member(channel_id, user_id)
#Make user Owner
await app.invoke(EditCreator(channel=channel_data,user_id=user_data,password=password))
await app.stop()
app.run(make_owner())
Extra Notes:
If you don't have your Telegram API Credentials you can get them in this link
Here's a sample code how you can do it
from pyrogram import Client
# Creating Pyrogram client
app = Client("my_account")
app.start()
# replace the below channel_username with Channel you want to transfer ownership of
channel = app.get_chat("channel_username")
# Get the ID of the user whom you want to transfer
new_owner_id = 00000 # Replace 00000 wit the id of new owner
# Get the current owner ID of the channel
current_owner_id = channel.owner_id
# Transfer Ownsership
app.edit_administrator(
chat_id=channel.id,
user_id=new_owner_id,
is_owner=True,
can_change_info=True,
can_invite_users=True,
can_delete_messages=True,
can_restrict_members=True,
can_pin_messages=True,
can_promote_members=True
)
# Revoke the old owner's admin permissions
app.edit_administrator(
chat_id=channel.id,
user_id=current_owner_id,
is_owner=False,
can_change_info=False,
can_invite_users=False,
can_delete_messages=False,
can_restrict_members=False,
can_pin_messages=False,
can_promote_members=False
)
# Logout
app.stop()

Passwordless authentication flow using Cognito & API Gateway & Lambda (Python)

I've been trying to implement passwordless authentication using AWS Cognito & API Gateway & Lambda (Python)
I have followed these articles:
https://medium.com/digicred/password-less-authentication-in-cognito-cafa016d4db7
https://medium.com/#pjatocheseminario/passwordless-api-using-cognito-and-serverless-framework-7fa952191352
I have configured Cognito (to accept CUSTOM_AUTH), added the Lambdas, and created the API endpoints:
/sign-up
/initiate-auth (aka initiate login)
/respond-to-auth-challenge (aka (verify login)
When calling initiateAuth I receive the following response:
An error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password."
I'm using CUSTOM_AUTH which doesn't require password, and the user name is definitely correct because it actually initiates the authentication flow and I receive a code, however because boto3 doesn't respond with a session I can't continue the authentication.
This is how I call Cognito:
res = cognito.initiate_auth(
ClientId=client_id,
AuthFlow="CUSTOM_AUTH",
AuthParameters={
"USERNAME": email,
"PASSWORD": random_password
}
)
It's probably something small I'm missing but I can't figure out what.
Your client code looks OK, mine has ClientId param in it but if your code is not raising an exception then it should be fine. Unless you had Generate client secret option checked when you created your app client.
If that is the case then you have to pass in SECRET_HASH in AuthParameters like the following:
import hmac
import hashlib
import base64
def get_secret_hash(email, client_id, client_secret):
"""
A keyed-hash message authentication code (HMAC) calculated using
the secret key of a user pool client and username plus the client
ID in the message.
"""
message = email + client_id
client_secret = str.encode(client_secret)
dig = hmac.new(client_secret, msg=message.encode('UTF-8'), digestmod=hashlib.sha256).digest()
return base64.b64encode(dig).decode()
client.admin_initiate_auth(
UserPoolId=COGNITO_USER_POOL_ID,
ClientId=CLIENT_ID,
AuthFlow='CUSTOM_AUTH',
AuthParameters={
'USERNAME': email,
'SECRET_HASH': get_secret_hash(email, CLIENT_ID, CLIENT_SECRET) # Omit if secret key option is disabled.
},
)
Next, double check the following:
Under App clients > * > Auth Flows Configuration, is ALLOW_CUSTOM_AUTH option enabled for your client?
Under App integration > App client settings > * > Enabled Identity Providers, is your user pool selected?
If you have Cognito setup correctly and your code still doesn't work then it is probably the lambda code. You probably know this but for password-less custom auth you need to use 3 lambda triggers: Define Auth Challenge, Create Auth Challenge, and Verify Auth Challenge.
Custom auth lambdas events are triggered in the following order:
DefineAuthChallenge_Authentication:
Technically, issueTokens can be set to True here to return tokens without going through the rest of the steps.
def lambda_handler(event, context):
if event['triggerSource'] == 'DefineAuthChallenge_Authentication':
event['response']['challengeName'] = 'CUSTOM_CHALLENGE'
event['response']['issueTokens'] = False
event['response']['failAuthentication'] = False
if event['request']['session']: # Needed for step 4.
# If all of the challenges are answered, issue tokens.
event['response']['issueTokens'] = all(
answered_challenge['challengeResult'] for answered_challenge in event['request']['session'])
return event
CreateAuthChallenge_Authentication:
def lambda_handler(event, context):
if event['triggerSource'] == 'CreateAuthChallenge_Authentication':
if event['request']['challengeName'] == 'CUSTOM_CHALLENGE':
event['response']['privateChallengeParameters'] = {}
event['response']['privateChallengeParameters']['answer'] = 'YOUR CHALLENGE ANSWER HERE'
event['response']['challengeMetadata'] = 'AUTHENTICATE_AS_CHALLENGE'
return event
Then your client must respond to the challenge:
client.respond_to_auth_challenge(
ClientId=CLIENT_ID,
ChallengeName='CUSTOM_CHALLENGE',
Session=session,
ChallengeResponses={
'USERNAME': email,
'ANSWER': 'Extra Protection!',
'SECRET_HASH': get_secret_hash(email, CLIENT_ID, CLIENT_SECRET) # Omit if secret key option is disabled.
}
)
VerifyAuthChallengeResponse_Authentication:
def lambda_handler(event, context):
if event['triggerSource'] == 'VerifyAuthChallengeResponse_Authentication':
if event['request']['challengeAnswer'] == event['request']['privateChallengeParameters']['answer']:
event['response']['answerCorrect'] = True
return event
DefineAuthChallenge_Authentication:
Set event['response']['issueTokens'] to True to return tokens (code shown in step 1), or issue another challenge to keep repeating steps 1-3.
Finally, make sure that if case-insensitivity option is enabled for your user pool too. Also, I can't exactly recall if CUSTOM_AUTH flow worked if the user is in FORCE_CHANGE_PASSWORD status. If the user is in that state, then try settings a permanent password with the sdk to set the status to CONFIRMED.
I was facing the same error, and I think that the error message is misleading.
When you did not respond correctly in Create-Auth-Challenge lambda, you will get this error. So make sure everything is right in your lambda.

How to store the contact's phone number in memory in ChatBot using python-telegram-bot

I am working with python-telegram-bot building a menu system.
I created a Django project, as shown below, using Webhook to connect to Telegram.
When the first message is sent, I ask the contact for their phone number and
keep this number so I don't need to ask for it in the next messages in the conversation,
but it’s not working.
I tried to store it in request.session, but apparently every new message is a new session and so I lose the number.
How can I solve this? Every help is welcome.
view.py
import json
from django.http.response import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from core.message import proccess
#csrf_exempt
def event(request):
json_telegram = json.loads(request.body)
proccess(request, json_telegram)
return HttpResponse()
messages.py
import telegram
from bot_webhook.settings import TOKEN
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
bot = telegram.Bot(token=TOKEN)
def proccess(request, json_telegram):
if 'login' in request.session:
msg_options(json_telegram)
else:
try:
request.session['login'] = json_telegram['message']['contact']['phone_number']
msg_options(json_telegram)
except BaseException:
msg_login(json_telegram)
def msg_login(json_telegram):
chat_id = json_telegram['message']['from']['id']
reply_markup = telegram.ReplyKeyboardMarkup(
[[telegram.KeyboardButton('Click to Login', request_contact=True)]],
resize_keyboard=True,
one_time_keyboard=True
)
bot.sendMessage(chat_id, 'I need to authorize your access.', reply_markup=reply_markup)
def msg_options(json_telegram):
chat_id = json_telegram['message']['from']['id']
first_name = json_telegram['message']['from']['first_name']
last_name = json_telegram['message']['from']['last_name']
button_list = []
button_list.append(InlineKeyboardButton('Button One', callback_data='query_one'))
button_list.append(InlineKeyboardButton('Button two', callback_data='query_two'))
reply_markup = InlineKeyboardMarkup(build_menu(button_list, n_cols=2))
bot.send_message(text='Hello {0} {1}!\nI have this options:'.format(first_name, last_name),
chat_id=chat_id,
reply_markup=reply_markup)
def build_menu(buttons,
n_cols,
header_buttons=None,
footer_buttons=None):
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
if header_buttons:
menu.insert(0, [header_buttons])
if footer_buttons:
menu.append([footer_buttons])
return menu
Once I haven't found a solution in Api, I worked with this solution:
When there is an interaction, I check if the contact already exists in my database,
if positive I continue the conversation,
if negative I ask for identification by sharing the phone.
So when I get the information, I record it in the database,
so that in a new interaction I can identify it automatically.
Record it in my database is it a option, another option is as text archive.
If you have a better option, please show it for us.

Send a local photo from inline mode in a telegram bot

I use Python telegram bot API for my bot.
I want to generate photos locally and send them as inline results but InlineQueryResultPhoto accepts only photo URLs.
Suppose my project structure looks like this:
main.py
photo.jpg
How do I send photo.jpg as an inline result?
Here is the code of main.py:
from uuid import uuid4
from telegram.ext import InlineQueryHandler, Updater
from telegram import InlineQueryResultPhoto
def handle_inline_request(update, context):
update.inline_query.answer([
InlineQueryResultPhoto(
id=uuid4(),
photo_url='', # WHAT DO I PUT HERE?
thumb_url='', # AND HERE?
)
])
updater = Updater('TELEGRAM_TOKEN', use_context=True)
updater.dispatcher.add_handler(InlineQueryHandler(handle_inline_request))
updater.start_polling()
updater.idle()
There is no direct answer because Telegram Bot API doesn't provide it.
But there are two workaounds: you can use upload a photo to telegram servers and then use InlineQueryResultCachedPhoto or you can upload to any image server and then use InlineQueryResultPhoto.
InlineQueryResultCachedPhoto
This first option requires you to previously upload the photo to telegram servers before creating the result list. Which options do you have? The bot can message you the photo, get that information and use what you need. Another option is creating a private channel where your bot can post the photos it will reuse. The only detail of this method is getting to know the channel_id (How to obtain the chat_id of a private Telegram channel?).
Now lets see some code:
from config import tgtoken, privchannID
from uuid import uuid4
from telegram import Bot, InlineQueryResultCachedPhoto
bot = Bot(tgtoken)
def inlinecachedphoto(update, context):
query = update.inline_query.query
if query == "/CachedPhoto":
infophoto = bot.sendPhoto(chat_id=privchannID,photo=open('logo.png','rb'),caption="some caption")
thumbphoto = infophoto["photo"][0]["file_id"]
originalphoto = infophoto["photo"][-1]["file_id"]
results = [
InlineQueryResultCachedPhoto(
id=uuid4(),
title="CachedPhoto",
photo_file_id=originalphoto)
]
update.inline_query.answer(results)
when you send a photo to a chat/group/channel, you can obtain the file_id, the file_id of the thumbnail, the caption and other details I'm going to skip. What the problem? If you don't filter the right query, you may end up sending the photo multiple times to your private channel. It also means the autocomplete won't work.
InlineQueryResultPhoto
The other alternative is upload the photo to internet and then use the url. Excluding options like your own hosting, you can use some free image hostings that provides APIs (for example: imgur, imgbb). For this code, generating your own key in imgbb is simpler than imgur. Once generated:
import requests
import json
import base64
from uuid import uuid4
from config import tgtoken, key_imgbb
from telegram import InlineQueryResultPhoto
def uploadphoto():
with open("figure.jpg", "rb") as file:
url = "https://api.imgbb.com/1/upload"
payload = {
"key": key_imgbb,
"image": base64.b64encode(file.read()),
}
response = requests.post(url, payload)
if response.status_code == 200:
return {"photo_url":response.json()["data"]["url"], "thumb_url":response.json()["data"]["thumb"]["url"]}
return None
def inlinephoto(update, context):
query = update.inline_query.query
if query == "/URLPhoto":
upphoto = uploadphoto()
if upphoto:
results = [
InlineQueryResultPhoto(
id=uuid4(),
title="URLPhoto",
photo_url=upphoto["photo_url"],
thumb_url=upphoto["thumb_url"])
]
update.inline_query.answer(results)
This code is similar to the previous method (and that includes the same problems): uploading multiple times if you don't filter the query and you won't have the autocomplete when writing the inline.
Disclaimer
Both code were written thinking the images you want to upload are generated at the moment you receive the query, otherwise you can do the work previous to receiving the query, saving that info in a database.
Bonus
You can run your own bot to get the channel_id of your private channel with pyTelegramBotAPI
import telebot
bot = telebot.TeleBot(bottoken)
#bot.channel_post_handler(commands=["getchannelid"])
def chatid(message):
bot.reply_to(message,'channel_id = {!s}'.format(message.chat.id))
bot.polling()
To get the id you need to write in the channel /getchannelid#botname

slack python api delete message

I am trying to use my bot to delete certain messages in a slack channel with this api call
import os
import time
import re
from slackclient import SlackClient
slack_client = SlackClient(
'xsssssseeeeeeee')
slack_mute_bot_id = None
def delete_message(slack_event):
for event in slack_event:
if event["type"] == "message":
message_text = event['text']
time_stamp = event['ts']
channel_id = event['channel']
slack_client.api_call(
'chat.delete',
channel=channel_id,
ts=time_stamp,
as_user=True
)
print(message_text + " delted")
if __name__ == "__main__":
if slack_client.rtm_connect(with_team_state=False):
slack_mute_bot_id = slack_client.api_call("auth.test")["user_id"]
while True:
# print(slack_client.rtm_read())
delete_message(slack_client.rtm_read())
time.sleep(1)
else:
print("Connection failed. Exception traceback printed above.")
I do not get any error message after doing this and the bot does not delete the message. I am using the bot user token. I have benn able to send message succesfully but the delete method does not work and still gives np responses
Refer - https://api.slack.com/methods/chat.delete
When used with a user token, this method may only delete messages
that user themselves can delete in Slack.
When used with a bot token, this method may delete only messages
posted by that bot.
I got stumbled into the same thing.

Categories

Resources