How to handle replies in with Pyrogram when using ForceReply? - python-3.x

I am building a telegram bot where I am attempting to get the user to fill in detail about an event and store them in a dictionary which is itself in a list.
However I want it be link a conversation. I want it to look like:
user: /create
bot-reply: What would you like to call it?
user-reply: Chris' birth day
bot-reply: When is it?
user-reply: 08/11/2021
bot-reply: Event Chris birth day on 08/11/2021 has been saved!
To achieve this I plan to use ForceReply which states in the documentation
This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode.
The problem is the documentation does not seem to explain how to handle responses.
Currently my code looks like this:
#app.on_message(filters.command('create'))
async def create_countdown(client, message):
global countdowns
countdown = {
'countdown_id': str(uuid4())[:8],
'countdown_owner_id': message.from_user.id,
'countdown_onwner_username': message.from_user.username,
}
try:
await message.reply('What do you want to name the countdown?',
reply_markup=ForceReply()
)
except FloodWait as e:
await asyncio.sleep(e.x)
Looking through the form I have found options like this:
python telegram bot ForceReply callback
which are exactly what I am looking for but they are using different libraries like python-telegram-bot which permit them to use ConversationHandler. It seems to not be part of pyrogram
How to I create user-friendly step-by-step interfaces with pyrogram?

Pyrogram doesn't have a ConversationHandler.
You could use a dict with your users' ID as the key and the state they're in as the value, then you can use that dictionary of states as your reference to know where your User is in the conversation.
Dan: (Pyrogram creator)
A conversation-like feature is not available yet in the lib. One way to do that is saving states into a dictionary using user IDs as keys. Check the dictionary before taking actions so that you know in which step your users are and update it once they successfully go past one action
https://t.me/pyrogramchat/213488

Related

Telethon utils.resolve_invite_link(link) is returning wrong Chat/Channel ID

I'm trying to generate the channel ID of my private channel in Telegram.
I use the following:
link = input("Please provide channel invite link: ")
print(utils.resolve_invite_link(link))
My output looks like the following (I've scrambled the numbers):
(0, 0123456789, 1234567891234567891)
When I view the private channel in the web browser, I get the channel ID as https://web.telegram.org/z/#-9876543210
So the channel ID should be -1009876543210, which I confirmed with IDBot.
Why isn't 9876543210 appearing when I call variable link within utils.resolve_invite_link()? That's the value I expected to see, not 0123456789.
utils.resolve_invite_link(link) no longer works with the new links.
Old links used to pack the channel/group ID inside the links themselves but this is no longer the case. The function will possibly be removed as well in future updates of the library https://github.com/LonamiWebs/Telethon/issues/1723
The most reliable way now is to use CheckChatInviteRequest https://tl.telethon.dev/methods/messages/check_chat_invite.html

How to get information out of a list

So iam coding an discord bot and i want to get information out of an list that looks like this and this output is in "information = await guild.invites()"
[<Invite code='GZqCe8EF' guild=<Guild id=847546806937059338 name='Testing' shard_id=None chunked=False member_count=2> online=None members=None>, <Invite code='jQ2HeQfx' guild=<Guild id=847546806937059338 name='Testing' shard_id=None chunked=False member_count=2> online=None members=None>]
is it possible to get single things out like guild id or maybe 2 things like name and invite code and is it possible that u can output every line from it like the first invite code and the second one?
To output every invite code, you can use:
for invite in information:
print(invite.code) #invite.code returns the invite code, do whatever you want to do with it
# you can also add invite.guild.name to for the guild name etc
To output only from one invite:
print(information[0].code)
References
invite object with every possible paramenter after .
Yes it is possible, the invites() method returns list of Invite objects. To check what attributes does this object have look here.
to access an attribute you have to do this: InviteObject.{name of attribute}. Example:
for invite in information:
print(invite.guild.id)
It works the same for any discordpy object:
Look for the method that you are using in discordpy docs (In your case invites())
See what does it return (In your case list of Invite objects)
Search for the object (In your case Invite)

Implementing an observer class in python

I am creating a bidding system where students can log in to a system and make a request for a tutor and sign a contract via a TKinter UI. I have managed to do this successfully. But now I am required to do the following,
"when a student or a tutor logs in into the system, the system should notify the user if there is a contract that is going to expire within a month."
I am having trouble trying to implement this feature as I feel this needs an observer and I for the life of me can't wrap my head around the Observer design pattern. I have no problem creating the methods to call for the contracts and determine which contract expires but I can't seem to figure out how to like display it using an observer. Can I get some guidance on this?
For reference, this is the method that would get all the contracts that are expiring within a month.
def expiring_contracts(self,username):
user_contracts = self.all_contracts_by_user(username)
expiring_contracts = []
for i in user_contracts:
time_string = i["expiryDate"]
time_datetime = datetime.strptime(time_string,'%Y-%m-%dT%H:%M:%S.%f')
one_month = datetime.now() + dateutil.relativedelta.relativedelta(months=1)
if time_datetime < one_month:
expiring_contracts.append(i)
return expiring_contracts
the self.all_contracts_by_user is another method within this class that gets all the contracts by this user. I must implement a design pattern for this.

Let Alexa ask the user a follow up question (NodeJS)

Background
I have an Intent that fetches some Data from an API. This data contains an array and I am iterating over the first 10 entries of said array and read the results back to the user. However the Array is almost always bigger than 10 entries. I am using Lambda for my backend and NodeJS as my language.
Note that I am just starting out on Alexa and this is my first skill.
What I want to archive is the following
When the user triggers the intent and the first 10 entries have been read to the user Alexa should ask "Do you want to hear the next 10 entries?" or something similar. The user should be able to reply with either yes or no. Then it should read the next entries aka. access the array again.
I am struggling with the Alexa implementation of this dialog.
What I have tried so far: I've stumbled across this post here, however I couldn't get it to work and I didn't find any other examples.
Any help or further pointers are appreciated.
That tutorial gets the concept right, but glosses over a few things.
1: Add the yes and no intents to your model. They're "built in" intents, but you have to add them to the model (and rebuild it).
2: Add your new intent handlers to the list in the .addRequestHandlers(...) function call near the bottom of the base skill template. This is often forgotten and is not mentioned in the tutorial.
3: Use const sessionAttributes = handlerInput.attributesManager.getSessionAttributes(); to get your stored session attributes object and assign it to a variable. Make changes to that object's properties, then save it with handlerInput.attributesManager.setSessionAttributes(sessionAttributes);
You can add any valid property name and the values can be a string, number, boolean, or object literal.
So assume your launch handler greets the customer and immediately reads the first 10 items, then asks if they'd like to hear 10 more. You might store sessionAttributes.num_heard = 10.
Both the YesIntent and LaunchIntent handlers should simply pass a num_heard value to a function that retrieves the next 10 items and feeds it back as a string for Alexa to speak.
You just increment sessionAttributes.num_heard by 10 each time that yes intent runs and then save it with handlerInput.attributesManager.setSessionAttributes(sessionAttributes).
What you need to do is something called "Paging".
Let's imagine that you have a stock of data. each page contains 10 entries.
page 1: 1-10, page 2: 11-20, page 3: 21-30 and so on.
When you fetching your data from DB you can set your limitations, In SQL it's implemented with LIMIT ,. But how you get those values based on the page index?
Well, a simple calculation can help you:
let page = 1 //Your identifier or page index. Managed by your client frontend.
let chunk = 10
let _start = page * chunk - (chunk - 1)
let _end = start + (chunk - 1)
Hope this helped you :)

Stripe: Getting Credit Card's Last 4 Digits

I have upgraded the Stripe.net to the latest version which is 20.3.0 and now I don't seem to find the .Last4 for the credit card. I had the following method:
public void CreateLocalCustomer(Stripe.Customer stipeCustomer)
{
var newCustomer = new Data.Models.Customer
{
Email = stipeCustomer.Email,
StripeCustomerId = stipeCustomer.Id,
CardLast4 = stipeCustomer.Sources.Data[0].Card.Last4
};
_dbService.Add(newCustomer);
_dbService.Save();
}
But now the stipeCustomer.Sources.Data[0].Card.Last4 says 'IPaymentSource' does not contain a definition for 'Card'. Does anyone know how I can get the card details now? The flow is that I create the customer by passing the Stripe token to Stripe, then I get the above stripeCustomer. So I expect it to be somewhere in that object. But I can't find it. The release notes can be found here.
Thank you.
In the old world of Stripe, there only used to be one type of payment method you could attach to a Customer; specifically, Card-objects. You would create a Card-object by using Stripe.js/v2 or the Create Token API Endpoint to first create a Token-object and then attach that token to a Customer-object with the Create Card API Endpoint.
Once Stripe expanded to support a number of other payment methods though, Stripe built support for a new object type that encapsulated a number of payment methods (including credit cards) called Source-objects. A Source-object is created either by using Stripe.js/v3 or the Create Source API Endpoint. It can also be attached to a Customer-object in much the same way as the Card-objects mentioned above, except they retain their object type. They're still a Source. You use the Attach Source API Endpoint to do this (that is notably identical to the Create Card API Endpoint mentioned above).
What I'm getting at here, is there are now two different object types (or more) that you can expect to see returned in the sources-array (or Sources in .NET). All of these methods though inherit from the IPaymentSource-interface. So if you know you have a Card-object getting returned, you can simply cast the returned object to the Card-class.
Something like this should get you going:
CardLast4 = ((Card) stipeCustomer.Sources.Data[0]).Last4
You can see what I mean by inheritance by looking at this line in the Card-class file:
https://github.com/stripe/stripe-dotnet/blob/master/src/Stripe.net/Entities/Cards/Card.cs#L7
Good luck!
As of Stripe.net.21.4.1, this is what works:
var chargeService = new ChargeService();
var charge = chargeService.Get(id);
CardLast4 = ((Card)charge.Source).Last4;
It's getting hard not to panic when code breaks because of all the micro-changes Stripe makes.
So after debugging, it looks like the Data[0] needs to be cast as Card to get the card.
So it will be CardLast4 = ((Card)stipeCustomer.Sources.Data[0]).Last4.

Resources