InlineKeyboardMarkup Telegram bot (pyTelegramBotAPI) - python-3.x

I am making a bot on pyTelegramBotAPI, I need to generate a list of buttons and write it into the keyboard (So that there are 5 buttons in a row). How should I do it? I have a code:
def start(message):
markup = types.InlineKeyboardMarkup()
buttons = []
for i in range(-12, 15):
if i < 0:
button = types.InlineKeyboardButton(f'{i}', callback_data=f'{i}')
elif i == 0:
button = types.InlineKeyboardButton(f'±{i}', callback_data=f'{i}')
else:
button = types.InlineKeyboardButton(f'+{i}', callback_data=f'{i}')
buttons.append(button)
if len(buttons) == 5:
# here i want to add buttons in a row
bot.send_message(message.chat.id, f'Здравствуйте, <b>{message.from_user.first_name}</b>. Для начала необходимо настроить часовой пояс. Выберите разницу во времени относительно UTC (Москва: "+3", Владивосток: "+10", Исландия: "+1")', parse_mode='html', reply_markup=markup)```

I can advise you to create a separate module responsible for keyboards.
example of modules
This way you can separate the logic of handlers from the logic of keyboards.
As torrua wrote earlier, you can use the keyboa (how to use keyboa) library.
or write the functions yourself to create keyboards and buttons in it.
What you need to know for this:
telebot.types.InlineKeyboardMarkup() # creates an empty keyboard.
telebot.type.InlineKeyboardButton() # creates a button for the keyboard created earlier.
.row() # determines the position of the buttons in the keyboard.
example of function to get keyboards

You can do it easy with keyboa:
from keyboa import Keyboa
def start():
buttons = []
for i in range(-12, 15):
if i < 0:
button = (f'{i}', i)
elif i == 0:
button = (f'±{i}', i)
else:
button = (f'+{i}', i)
buttons.append(button)
keyboard = Keyboa(items=buttons, items_in_row=5).keyboard
bot.send_message(message.chat.id, f'Здравствуйте, <b>{message.from_user.first_name}</b>. Для начала необходимо настроить часовой пояс. Выберите разницу во времени относительно UTC (Москва: "+3", Владивосток: "+10", Исландия: "+1")', parse_mode='html', reply_markup=keyboard)```

Related

Python Keyboard module detects the pressed key more than once

I'm trying to make a keylogger with the 'Keyboard' module in Python, the troubble is that the read_key() function is returning the same pressed key many times.
import keyboard as kb
cadena = ''
while True:
key = kb.read_key()
if len(key) == 1:
cadena +=key
print(cadena)
I just wrote "Hello world"
but this code returns:
h
hh
hhe
hhee
hheel
hheell
hheelll
hheellll
hheellllo
hheelllloo
hheelllloow
hheelllloowo
hheelllloowow
hheelllloowowo
hheelllloowowor
hheelllloowoworr
hheelllloowoworrl
hheelllloowoworrll
hheelllloowoworrlld
hheelllloowoworrlldd
You can use keyboard.record to record all keyboard activity until a specified key is pressed. After this you can either replay the activity or print particular events (e.g. down press events):
import keyboard
text = ""
rec = keyboard.record(until='Enter')
# Either this
keyboard.play(rec)
# Or this
for event in rec:
if event.event_type == "down":
text += event.name
print(text)

Python-Telegram-Bot: How to wait for user input after clicking on InlineKeyboardButton

I'm working on a bot with the Python-Telegram-Bot API. Right now I'm updating the bot menu with InlineKeyboardButtons instead of ?menu, ?info type of commands (like on Discord). But I came across some trouble. Let me explain step by step what the command is supposed to do:
Step 1 - User opens up the menu (a commandhandler)
Step 2 - InlineKeyboardButtons load, one of them has an option called "Jokenpo" for the game
Step 3 - I set its callback_data to load a message greeting the player and showing the rules (def jkpMenu).
Step 4 - Then def jkpMenu is supposed to go to the next state, that is def jokenpo. During the function, there's a loop for receiving input like paper, rock, scissors that keeps repeting until user types stop or loses their lives.
varScore = 0
varLives = 5
varTie = 0
JKP = range(2)
...
def menuTest(update: Update, _: CallbackContext) -> int:
commands = [
[InlineKeyboardButton(text='📑 | Changelog', callback_data='clog'), InlineKeyboardButton(text='📑 | Info', callback_data='info')],
[InlineKeyboardButton(text='📑 | Jokenpo', callback_data='jokenpo')]]
update.message.reply_text("Menu:\n", reply_markup=InlineKeyboardMarkup(commands))
def comQuery(update: Update, context: CallbackContext) -> None:
query = update.callback_query
query.answer()
if query.data == 'clog': changelog(update, context)
if query.data == 'info': myInfo(update, context)
if query.data == 'jokenpo': jkpMenu(update, context)
#Jokenpo 1: greetings and rules
def jkpMenu(update: Update, _: CallbackContext) ->JKP:
update.callback_query.message.reply_text('Jokenpo mode. Please type "rock", "paper" or "scissors" to continue')
return JKP
#Jokenpo 2:
def jokenpo(update: Update, _:CallbackContext):
msgUser = update.callback_query.message.text.lower()
global varScore
global varLives
global varTie
while True:
computer = ("rock", "paper", "scissors")
computer = random.choice(computer)
if (msgUser== "rock" and computer == "paper") or (msgUser== "paper" and computer == "scissors") or (msgUser=="scissors" and computer == "rock"):
update.callback_query.message.reply_text("Computer chooses <i>" + computer + "</i>. <b>You lose!</b>", parse_mode ='HTML')
varLives -= 1
if (msgUser== "rock" and computer == "scissors") or (msgUser == "paper" and computer == "rock") or (msgUser == "scissors" and computer == "paper"):
update.callback_query.message.reply_text("Computer chooses <i>" + computer + "</i>. <b>You win!</b>", parse_mode ='HTML')
varScore +=1
if (msgUser== computer):
update.callback_query.message.reply_text("We both chose <i>" + computer +"</i>. <b>It's a tie!</b>", parse_mode ='HTML')
varTie +=1
if (msgUser== "status"):
update.callback_query.message.reply_text("Your current score: " + str(varScore) + " points.\nYou have "+ str(varLives)+ " lives left.\nWe tied " + str(varTie) +' times.')
if (msgUser== "sair") or (varLives == 0):
update.callback_query.message.reply_text("Game finished. You made <b>" + str(varScore) + "</b> points.\nWe tied " + str(varTie) +' times.\n', parse_mode ='HTML')
varScore = 0
varTie = 0
varLives = 5
return ConversationHandler.END
update.callback_query.message.reply_text('Please choose: paper, rock or scissors? ')
return
...
...
def(main):
convHandler = ConversationHandler(
entry_points=[jokenpo],
fallbacks=[],
states = {
JKP: [MessageHandler(Filters.text, jokenpo)]
})
dp.add_handler(convHandler)
Problem is: I managed to do that before, when I used CommandHandlers to access the function and it worked.
But now it only reaches the first function (Step 3), it seems it doesn't return to the state I wanted. When I changed return JKP for return jokenpo(update,_) it did access def jokenpo, but it didn't wait for any answer.
When I set the inline buttons, since they use CallbackContext, I'm confused on how to handle arguments.
I copied only some part of the code, not it complete, since the other parts are related to other functions. Any help is appreciated.
I see several issues in your code snippet & description:
using global variables is in general bad practice. If you want to store user/chat related data, I suggest to use PTBs built-in mechanism for that. see here.
The while True loop in jokenpo doesn't make any sense. In fact, at the end of the body of the loop, you return anyway, so the body is executed exactly once.
As entry points for your conversation you use entry_points = [jokenpo], but jokenpo is a function and not a handler
In the state JKP you have a handler MessageHandler(Filters.text, jokenpo). This means that the update that will be passed to jokenpo will have update.(edited_)message/channel_post, but never update.callback_query - yet you try to access this attribute within jokenpo.
"Then def jkpMenu is supposed to go to the next state, that is def jokenpo" - the callback jkpMenu is not part of your ConversationHandler at all and hence the return value will be ignored completely.
"When I changed return JKP for return jokenpo(update,_) it did access def jokenpo, but it didn't wait for any answer." Sure - you called the function. But waiting for input can only work if the conversationhandler is started, which this doesn't do
"When I set the inline buttons, since they use CallbackContext, I'm confused on how to handle arguments." This one I just don't understand. What do you mean by "arguments" in the context of inline buttons and what does that have to do with CallbackContext?
I have the impression that you need to deepen your understanding of how PTB is supposed to be used, especially ConversationHandler. I suggest to have a look at the introductory example. Following the flow chart & tracking the executed code while running the example should help to get a better understanding of what's going on. Also reading the documentation of ConversationHandler should clarify some things.
Disclaimer: I'm currently the maintainer of python-telegram-bot.
Also for completeness sake: This was already discussed to some extend in the usergroup of PTB, see https://t.me/pythontelegrambotgroup/513856

How to Call a Function with 'on_press' command inside same class using KIVY button

I'm trying to call a popup and the have the user input text. After hitting the save button inside my popup it updates a new widget.
I have almost everything working but I don't know how to properly call a function inside the same class.
I have two functions inside of my
'class Trackers(Screen):'
how can I make a button that 'on_press' command will trigger another function?
The line of code I need to be fixed is this:
okay = Button(text='Save', on_press=self.addit(str(text_input), num), on_release=pop.dismiss)
Thank you so much.
''''
def addWidget(self):
num = '0'
box = BoxLayout(orientation= 'vertical', padding=40, spacing=5)
pop = Popup(title='Tracker: details', content=box, size_hint=(None,None), size=(300,300))
text_input = TextInput(hint_text='Add Trackers', multiline=False,)
okay = Button(text='Save', on_press=self.addit(str(text_input), num), on_release=pop.dismiss)
box.add_widget(text_input)
box.add_widget(okay)
pop.open()
def addit(self, text_input, num, *args):
if text_input not in self.storage.keys():
self.ids.track.add_widget(Tracker(text=text_input, number=num, data=self.storage))
self.storage[text_input] = '0'
text_input = ''
self.saveData()
else:
box = BoxLayout(orientation= 'vertical', padding=40, spacing=5)
pop = Popup(title=f'Opps: "{text_input}" is already listed below', content=box, size_hint=(None,None), size=(300,180))
try_again = Button(text='Try Again', on_release=pop.dismiss)
box.add_widget(try_again)
pop.open()
''''

Error using checkmouse

I'm trying to use checkmouse in order to undraw something from my window. When someone clicks the button it should undraw the text and write something else. I'm using checkMouse and getX() and getY() to do this but i keep receiving this error that states:
File "C:\Users\User\Documents\python\project2.py", line 71, in panel
if (clicknew.getX()>90 and clicknew.getX()<210) and (clicknew.getY()>35 and clicknew.getY() < 0):
AttributeError: 'NoneType' object has no attribute 'getX'
this code that i have done so far is as follows:
from graphics import *
#creating the game panel window
def panel():
#grey window, with coordinates flipped, with banners etc
win = GraphWin("Start Panel", 300,200)
win.setCoords(0,0,300,200)
win.setBackground("light grey")
#drawing the BoilerMazer banner with text
boilermazer = Rectangle(Point(0,200),Point(300,160))
boilermazer.setFill("white")
boilermazer.draw(win)
#text inside
banner1 = Text(Point(150,180),"BoilerMazer")
banner1.setStyle("bold")
banner1.setSize(20)
banner1.draw(win)
#initial game panel is going to have two buttons and a top scores object
#top score "screen"
toprec = Rectangle(Point(60,140),Point(240,50))
toprec.setFill("white")
toprec.draw(win)
#text inside toprec
topscores = Text(Point(150,130),"TOP SCORES")
topscores.setSize(8)
topscores.draw(win)
border = Text(Point(150,120),"======")
border.draw(win)
bigmac = Text(Point(150,110),"Big Mac 21")
bigmac.setSize(8)
bigmac.draw(win)
tt = Text(Point(150,90),"T.T 23")
tt.setSize(8)
tt.draw(win)
cshell = Text(Point(150,75),"C-Shell 25")
cshell.setSize(8)
cshell.draw(win)
macmac = Text(Point(150,55),"MacMac 27")
macmac.setSize(8)
macmac.draw(win)
#new player button that will eventually be clicked
new1 = Point(90,35)
new2 = Point(210,0)
newrec = Rectangle(new1,new2)
newrec.setFill("chartreuse2")
newrec.draw(win)
#new player button text
newplayer = Text(Point(150,18),"NEW PLAYER")
newplayer.draw(win)
#reset button
resetrec = Rectangle(Point(240,35),Point(300,0))
resetrec.setFill("red")
resetrec.draw(win)
#resettext
reset = Text(Point(270,18),"RESET")
reset.draw(win)
#secondary panel window is the game panel after they click new player
#set up points that we check between for the new player button first
#setting up the checkmouse
clicknew = win.checkMouse()
if (clicknew.getX()>90 and clicknew.getX()<210) and (clicknew.getY()>35 and clicknew.getY() < 0):
newplayer.undraw()
you can find the graphics window here:http://mcsp.wartburg.edu/zelle/python/graphics.py
I don't understand what I'm doing wrong, is there some other method that I'm supposed to be using? Thanks for your help
According to the docs, checkMouse() returns None if no mouse click has been detected priorly. So that seems to be the case.
You could put a loop around the call to checkMouse and keep checking if clicknew is not None and only in that case go on in your program. But maybe there's a better way...
UPDATE:
Example:
while True:
clicknew = win.getMouse()
if clicknew:
break
else:
time.sleep(0.1) # avoid busy waiting
# clicknew is set now => use it

How to manually set the toggle state of wxpython platebutton

I am building an application using platebutton iin wxpython. The problem is that I am not able to manually SetState of the toogle buton. I used SetState(0) but it does not change the state of toggle button. Any help would be great. Thanks. Sample code:
self.infinity= platebutton.PlateButton(self._ribbon,wx.ID_NEW, bmp = wx.Bitmap('infinity.bmp'), pos = (0,0), size = (38,18), style= platebutton.PB_STYLE_NOBG |platebutton.PB_STYLE_TOGGLE)
def OnInfinityToggled(self,event):
if event.GetEventObject().IsPressed():
self.popupmenu = wx.Menu()
Session = self.popupmenu.Append(-1, "Session")
self.Bind(wx.EVT_MENU, self.SessionMenu, Session)
self.PopupMenu(self.popupmenu,(2,23))
else:
pass
def SessionMenu(self, event):
print 5
self.infinity.SetState(0)
self.infinity.Raise()
PLATE_NORMAL = 0
PLATE_PRESSED = 1
PLATE_HIGHLIGHT = 2
SetState(0) means set to normal.
Here's how I managed to toggle state:
btn._ToggleState()
btn._pressed = True
I had the same problem. Playing around I managed to resolve my problem with
button._SetState(ix)
button.Refresh()
where ix = your choice of state.

Resources