I have a bot in Python that prompts the user to pick from a list of IDs to change and the idea is that the user will select the ID and then supply the ID they want to change to.
Here is my code:
def build_menu(buttons, n_cols):
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
return menu
def change_process(bot, update):
global action
query = update.callback_query
NAME = query.data
if action == 'Change':
# Prompt user for new value
# run sql query to update
def change(bot, update, args):
global action
action = 'Change'
sql = sqlite3.connect('SQL_FILE.db',detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cur = sql.cursor()
button_list = [InlineKeyboardButton(s[0], callback_data=s[0]) for s in cur.execute('SELECT Name FROM ID ORDER BY Name ASC')]
reply_markup = InlineKeyboardMarkup(build_menu(button_list, n_cols=3))
update.message.reply_text('ID to change:', reply_markup=reply_markup)
action = None
updater = Updater(token='XXXX:XXXX')
dispatcher = updater.dispatcher
dispatcher.add_handler(CallbackQueryHandler(change_process))
arg_handler = CommandHandler('change', change, pass_args=True)
dispatcher.add_handler(arg_handler)
Here is how I want the program to work:
User runs /change
Bot returns list of IDs that are able to be changed (using InlineKeyboardMarkup() and build_menu()
User picks one of the ids
Bot prompts user for new value
The next message that the user sends to the bot will be the value to use
Bot will use the ID the user selected as well as new value and run query to update in database
Bot comments that it updated ID with new value of 'USER VALUE'
The problem I am having is that I can prompt the user to pick the ID and I can prompt the user for input, but I do not know how to do both and use both values in a single function.
I believe you need to look to conversation examples that python-telegram-bot library provides:
conversationbot.py
conversationbot2.py
nestedconversationbot.py
There are also diagrams that explains the flow of asking data from user.
Related
I am building a small Jupyter Notebook widget to call a Rest API and get some data.
The user need to provide an auth token to be able to call the API. The auth token (JWT Token) will be provided to the user from a different interface. The API will respond with a json list of data which needs to be put into a combobox and then user can choose 1 which will be inserted into a new cell
The issue I am running into is that I am unable to tell the API call to wait for user to input the auth token and click on the submit button.
There are 2 actions happening here
Act 1 : Text Box and Button for the auth token. User puts the auth token in the text box and clicks submit.
Auth token gets put into a global variable by the button on clicked function
Act 2: Use the global variable to call the Rest API to get the JSON list of data
Put the list into a combobox
User chooses a specific value.
Dump that specific data into a new cell
import requests
import json
from IPython.display import Javascript, display
def click_get_auth_token(b):
global get_datasources_api_token
get_datasources_api_token = atok.value
return None
def show_wid():
global atok
atok = widgets.Text(
placeholder='Auth_Token',
description='Auth Token',
disabled=False
)
display(atok)
button = widgets.Button(description="Submit")
display(button)
button.on_click(click_get_auth_token)
return None
def click_get_auth_token():
show_wid()
headers = {}
resp = requests.get('http://localhost:5000/get_data', headers={"Authorization" : "Bearer " + get_datasources_api_token})
resp = resp.json()
dsr_list = resp['datasources']
opt_list = []
for dsr in dsr_list:
opt_list.append(dsr["name"])
print(opt_list)
global w
w = widgets.Combobox(
options= opt_list,
disabled=False,
ensure_option=True,
)
display(w)
button = widgets.Button(description="Get Data")
display(button)
button.on_click(click_get_datasources)
def click_get_datasources()
dataset = None
for dsr in dsr_list:
if(dsr["name"] == w.value):
dataset = dsr
encoded_code = (base64.b64encode(str.encode(dataset))).decode()
display(Javascript("""
var code = IPython.notebook.insert_cell_below('code');
code.set_text(atob("{0}"));
""".format(encoded_code)))
My code is probably all over the place and is needlessly complex.
At the moment it works as long as I hard code an auth token.
But I am unable to get the Rest call to wait for the user to paste the token.
Any help would be appreciated.
Thank you
Here's my code:
from telegram import *
from telegram.ext import *
import telegram, telegram.ext
u = Updater('TOKEN', use_context=True)
j=u.job_queue
def set(update: Update, context: CallbackContext):
user_says=' '.join(context.args)
user_says=user_says.split(' ')
global item, chat_id
item=user_says[1:]
chat_id=update.effective_chat.id
j.run_once(callback, int(user_says[0]))
def callback(context: telegram.ext.CallbackContext):
context.bot.send_message(chat_id=chat_id, text=f'Great you won {item}')
if __name__ == '__main__':
dp = u.dispatcher
dp.add_handler(CommandHandler('set', set))
u.start_polling()
u.idle()
So the problem is if someone start the timer and in the mean time when the timer is running someone else (or the same person) start different timer the value of item get replaced with the newest one, it will display wrong item value for all the timers which are already running in that time (except for the latest one) how can I store items and show them with their corresponding timer's callback
Hope you understood my question :)
You may simply use a dict to store the items and the chat_id with an UUID as a key, then, pass the UUID to the callback function and retrieve the items from the dict.
from uuid import uuid4
items = {}
def set(update: Update, context: CallbackContext):
user_says=' '.join(context.args)
user_says=user_says.split(' ')
chat_id=update.effective_chat.id
uid = uuid4()
items[uid] = {"id": chat_id, "item": user_says[1:]}
j.run_once(callback, int(user_says[0]), context=uid)
def callback(context: telegram.ext.CallbackContext):
uid = context.job.context
chat_id = items[uid]["id"]
context.bot.send_message(chat_id=chat_id, text=f'Great you won {items[uid]["item"]}')
del items[uid]
Read more about multi-users handling in How to manage more users in a telegram bot?
IMHO Knugis answer is unnecessarily complicated. Instead of storing some data in a global dict and then passing the key to context, you might as well just pass the data directly, e.g. as tuple:
j.run_once(callback, int(user_says[0]), context=(chat_id, user_says[1:]))
and then
def callback(context: telegram.ext.CallbackContext):
uid, user_says = context.job.context
...
On a somewhat related note I'd like to point out that PTB already comes with a built-in mechanism to store user/chat-related data. See this wiki page for details.
Disclaimer: I'm currently the maintainer of python-telegram-bot.
I want to batch create users for admin. But, the truth is make_password is a time-consuming task. Then, if I returned the created user-password list util the new users are all created, it will let front user waiting for a long time. So, i would like to do somethings like code showed below. Then, I encountered a problem. Cause I can not figure out how to lock the user_id_list for creating, someone registered during the thread runtime will cause an Dulplicate Key Error error thing like that.
So, I am looking forward your good solutions.
def BatchCreateUser(self, request, context):
"""批量创建用户"""
num = request.get('num')
pwd = request.get('pwd')
pwd_length = request.get('pwd_length') or 10
latest_user = UserAuthModel.objects.latest('id') # retrieve the lastest registered user id
start_user_id = latest_user.id + 1 # the beginning user id for creating
end_user_id = latest_user.id + num # the end user id for creating
user_id_list = [i for i in range(start_user_id, end_user_id + 1)] # user id list for creating
raw_passwords = generate_cdkey(num, pwd_length, False) # generating passwords
Thread(target=batch_create_user, args=(user_id_list, raw_passwords)).start() # make a thread to perform this time-consuming task
user_password_list = list(map(list, zip(*[user_id_list, raw_passwords]))) # return the user id and password immediately without letting front user waiting so long
return {'results': user_password_list}
I need your help to order listed item.
I am trying to make apps that can send message to his/her friends ( just like social feeds ). After watching Bret Slatkin talk about create microblogging here's my code:
class Message(ndb.Model):
content = ndb.TextProperty()
created = ndb.DateTimeProperty(auto_now=True)
class MessageIndex(ndb.Model):
receivers = ndb.StringProperty(repeated=True)
class BlogPage(Handler):
def get(self):
if self.request.cookies.get("name"):
user_loggedin = self.request.cookies.get("name")
else:
user_loggedin = None
receive = MessageIndex.query(MessageIndex.receivers == user_loggedin)
receive = receive.fetch()
message_key = [int(r.key.parent().id()) for r in receive]
messages = [Message.get_by_id(int(m)) for m in message_key]
for message in messages:
self.write(message)
The first I do a query to get all message that has my name in the receivers. MessageIndex is child of Message, then I can get key of all message that I receive. And the last is I iter get_by_id using list of message key that I get.
This works fine, but I want to filter each message by its created datetime and thats the problem. The final output is listed item, which cant be ordered using .order or .filter
Maybe some of you can light me up.
You can use the message keys in an 'IN' clause in the Message query. Note that you will need to use the parent() key value, not the id() in this case.
eg:
# dtStart, dtEnd are datetime values
message_keys = [r.key.parent() for r in receive]
query = Message.query(Message._key.IN(message_keys), Message.created>dtStart, Message.created<dtEnd)
query = query.order(Message.created) # or -Message.created for desc
messages = query.fetch()
I am unsure if you wish to simply order by the Message created date, or whether you wish to filter using the date. Both options are catered for above.
I'm trying to get a field from openERPs mail_message model using python code which is executed in a server action (so its not a module where I can debug! I cannot even print in this state) (when a new eMail is being fetched) but I am unable to get anything useful from it.
Basicly when someone is throwing me a email, a new Task is created by openERP. But the newely created ticket is not connected to the user which send me the mail.
When a new email is fetched, this server action gets executed.
In a table called mail_message you can then find the email (+ author_id, + email, + res_id (which is the id of the created Task), therefore I'd like to fetch the author_id from that table.
(A query would look like this:
SELECT author_id FROM mail_message WHERE type = 'email' AND res_id = '<Task.id>')
This is my current code
#Initialize object. That one points to the mail_message model.
mailMessage_obj = self.pool.get('mail.message')
#Created Id in project_task
myId = object.id
#browse whole object with that id
#message = mailMessage_obj.browse(cr,uid,[myId])
#Select field where
messageIds = mailMessage_obj.search(cr,uid,[('type','=','email'),('res_id','=',myId)],context=context)
if messageIds:
#messageRecord = mailMessage_obj.browse(cr,uid,[myId],context=context)
#object.write({'partner_id':messageRecord.author_id.id})
res = mailMessage_obj.read(messageIds, ['author_id'])
partnerId = res[0]
#Author id
#partnerId = message[0]['author_id']
#partnerId = message.author_id
#res = [(r['id'], r['author_id']) for r in messageRecord]
#partnerId = res
#partnerId = 259866
object.write({'partner_id':partnerId})
I dont know how to get my hands on the author_id properly. If I hardcode a ID and let it write to the database (last two lines) It'll work just fine, but I cant hardcode a users id. ;)
Could someone explain to me how its done correctly?
I dont know whether I should use .browse or .read or something else..
I think you have an error on you python method.
you wrote :
res = mailMessage_obj.read(messageIds, ['author_id'])
partnerId = res[0]
But read() method returns here a list of dict (because messageIds is a list). Then, you have not specified the field you wanted to retrieve from res variable, and finally, as author_id is a many2one, it returns something like this : (2, 'myusers').
You should have done :
res = mailMessage_obj.read(cr, uid, messageIds, ['author_id'])
partnerId = res[0]['author_id'][0]
Hope i helped you,
Greatings