How To Change Conversation Handler State with a CallbackQueryHandler Button function - python-telegram-bot

I have a python telegram bot that sends messages with Buttons on them like this:
keyboard = [[telegram.InlineKeyboardButton("Finish", callback_data='1'),
telegram.InlineKeyboardButton("Edit", callback_data='2')]]
update_obj.message.reply_text(message_text, reply_markup=reply_markup,
parse_mode=telegram.ParseMode.MARKDOWN)
Now when the user hits the "Edit" button I want the ConversationHandler to move to another state.
Here is a part of my ConversationHandler:
WELCOME = 0
SEARCH = 2
EDITTITLE = 3
[...]
handler = telegram.ext.ConversationHandler(
entry_points=[telegram.ext.CommandHandler('start', start)],
states={
WELCOME: [
telegram.ext.MessageHandler(filters=telegram.ext.Filters.all, callback=welcome),
updater.dispatcher.add_handler(telegram.ext.CallbackQueryHandler(button))
],
SEARCH: [
telegram.ext.MessageHandler(filters=telegram.ext.Filters.all, callback=search),
updater.dispatcher.add_handler(telegram.ext.CallbackQueryHandler(button))
], EDITTITLE: [
telegram.ext.MessageHandler(filters=telegram.ext.Filters.all, callback=editTitle)
]
[...]
Now normally I can just return the new state in the callback function from the MesageHandler like so:
def search(update_obj, context):
[...]
return WELCOME
But if I return the new state in my button function it does not switch to that state. Here's the code:
def button(update_obj, context):
[...]
return EDITTITLE
How can I make the button change the ConversationHandler state or is there something I am missing?
Am I using the Buttons the right way?
Thanks :)

You're adding the CallbackQueryHandlers wrongly to the conversation. updater.dispatcher.add_handler(telegram.ext.CallbackQueryHandler(button)) returns None, so None will be added to the list of handlers in the states WELCOME and SEARCH. Instead it should be just telegram.ext.CallbackQueryHandler(button).
It could be that there are additional issues, but I'd have to see a full minimal working example to judge that.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

Related

How to use Button in discord.py

I can't figure out how to do it in discord.py so that pressing a button reads the user who pressed it, then writes all users who pressed the button to a temporary array. And if it's not difficult if you know YouTube lessons on this, please give a link. (translated with google translator, sorry)
You can add a callback function to your button for that, like this:
#bot.command()
async def button(ctx):
users = []
button = discord.ui.Button(style = discord.ButtonStyle.primary, label="Add a user")
async def button_callback(i):
users.append(i.user)
print(users)
button.callback = button_callback
# Need to create a view
buttons_view = discord.ui.View()
# and add the button component to the view
buttons_view.add_item(button)
# send the view with the message
await ctx.send("Add a user by clicking the button", view=buttons_view)

How to get DetailsList/Selection component on React Fabric UI to work with ReactHooks?

I'm having issue getting the React Fabric UI DetailsList to work with React Hooks. It renders, but the selection part does not. Whenever, you select a row, I expect the count of the selection to be updated. However, i'm not seeing that. It looks like the selection component items never get updated even thou the UI shows it being selected. I'm also seeing the onSelectionChange being triggered when you select a row. Now sure if its because i'm using react hooks or what
I took the provided class example which works:
[Codepen]https://codepen.io/pen/?&editable=true (Example)
Same as the original but stripped down
[Codepen]https://codepen.io/darewreckk/pen/NQRWEd?editors=1111
converted it to a react hook component. I would expect the same thing, but i'm not and can't figure out what's different.
Modified Example with Hooks
[Codepen]https://codepen.io/darewreckk/pen/GVjRNb?editors=1111
Any advice appreciated,
Thanks,
Derek
The selection count is stored on the Selection object that you are creating. You could log it out like so:
const _selection = new Selection({
onSelectionChanged: () => {
console.log('Selected count:' + _selection.count);
}
});
You can set the value of selectionCount and selectionDetails whenever onSelectionChanged is called:
const _selection = new Selection({
onSelectionChanged: () => {
setSelectionCount(_selection.count);
setSelectionDetails(`${_selection.count} items selected`)
}
});
As a side note: If you want selectionDetails to update when selectionCount updates, you can use the useEffect hook:
React.useEffect(() => {
setSelectionDetails(`${selectionCount} items selected`);
}, [selectionCount]);
The above effect will run whenever selectionCount updates, and so in turn update selectionDetails.

SAPCAI quick replies displayed as attachments

I used SAPCAI (SAP Conversational AI is a French development platform like Azure Bot Service) to build my chatbot, but I'm using the Bot-Framework Webchat on my web app. Therefore, I don't have any C# or JS code. The problem is SAPCAI quick replies are displayed as "attachments". How can I fix that?
Expected
Got
What you want is the Quick replies type.
Quick replies: Same purpose as buttons, but disappear once clicked. Great if you don’t want the user to have to scroll up the conversation and click on a button again.
There is information about how to create click replies on this page. It seems to be in the format of:
{
"type": "quickReplies",
"content": {
"title": "TITLE",
"buttons": [
{
"title": "BUTTON_TITLE",
"value": "BUTTON_VALUE"
}
]
}
}
The important part would be "type": "quickReplies". Since you haven't provided any code I'm not sure if you know how to get to the stage where you enter/edit this JSON. From the documentation on the first page that I linked it would seem that you get to this via:
On the Actions tab of a skill (or on the Requirements tab), you can choose among other things to send messages.
Under the send message button you will be displayed a list of message types to send, quick replies is one of these types. See my screenshots here.
I hope this helps.
I'm not entirely sure how SAPCAI works, but if you are receiving a card in Web Chat, you can use a custom middleware store to convert the card's title to text and its buttons to suggested actions. Then you can add them to the activity in place of the attachment. Note, the store middleware below will convert all card to suggested actions, so you may want to add some additional logic if you do intend to use other cards in your dialog. Also, if the card is an AdaptiveCard, you will need to do some more modifications as well since those tend to more complex than rich cards.
Middleware
const store = createStore(
{},
({ dispatch}) => next => async action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { attachments, from: { role }} = action.payload.activity;
if (role === 'bot' && attachments) {
const text = attachments.map(({ content: { title }}) => title).join(' ');
const actions = attachments.map(({content: { buttons }}) => buttons).flat();
action.payload.activity.text = text;
action.payload.activity.attachments = [];
action.payload.activity.suggestedActions = { actions };
}
}
return next(action)
}
);
renderWebChat({
directLine,
store,
}, document.getElementById('webchat'));
Screenshot
Hope this helps!

Firefox WebExtension notifications API: How to call a function when the notification is clicked

I'm trying to develop a Firefox add on using WebExtensions. What I'm trying to do is open a new Firefox tab or window when the user clicks the notification. But it doesn't work.
When I click the notification, nothing happens.
I'm creating notifications like:
var q = chrome.notifications.create({
"type": "basic",
"iconUrl": chrome.extension.getURL("128-128q.png"),
"title": 'title',
"message": 'content'
});
chrome.notifications.onClicked.addListener(function(notificationId) {
window.open('http://www.google.com');
});
browser.notifications.onClicked.addListener(function(notificationId) {
window.open('http://www.google.com');
});
q.onClicked.addListener(function(notificationId) {
window.open('http://www.google.com');
});
var audio = new Audio('message.mp3');
audio.play();
How can I make this work?
It appears that the problem is that you are attempting to open a new URL in a way that does not work.
The following should work, using chrome.tabs.create():
var q = chrome.notifications.create("MyExtensionNotificationId", {
"type": "basic",
"iconUrl": chrome.extension.getURL("128-128q.png"),
"title": 'title',
"message": 'content'
});
chrome.notifications.onClicked.addListener(function(notificationId) {
chrome.tabs.create({url:"http://www.google.com"});
});
However, you need to be testing this in Firefox 47.0+ as support for chrome.notifications.onClicked() was only added recently. My statement of Firefox 47.0+ is based on the compatibility table. However, the compatibility table has at least one definite error. Thus, a higher version of Firefox may be required. The code I tested worked in Firefox Nightly, version 50.0a1, but did not work properly in Firefox Developer Edition, version 48.0a2. In general, given that the WebExtensions API is in active development, you should be testing against Firefox Nightly if you have questions/issues which are not functioning as you expect. You can also check the source code for the API to see what really is implemented and when it was added.
Note: this works with either chrome.notifications.onClicked or browser.notifications.onClicked. However, don't use both to add two separate anonymous functions which do the same thing as doing so will result in whatever you are doing happening twice.
I did not actually test it with your code, but I did test it with a modified version of the notify-link-clicks-i18n WebExtensions example. I modified the background-script.js file from that example to be:
/*
Log that we received the message.
Then display a notification. The notification contains the URL,
which we read from the message.
*/
function notify(message) {
console.log("notify-link-clicks-i18n: background script received message");
var title = chrome.i18n.getMessage("notificationTitle");
var content = chrome.i18n.getMessage("notificationContent", message.url);
let id = "notify-link-clicks-i18n::" + title + "::" + message.url;
chrome.notifications.create(id,{
"type": "basic",
"iconUrl": chrome.extension.getURL("icons/link-48.png"),
"title": title,
"message": content
});
}
/*
Assign `notify()` as a listener to messages from the content script.
*/
chrome.runtime.onMessage.addListener(notify);
//Add the listener for clicks on a notification:
chrome.notifications.onClicked.addListener(function(notificationId) {
console.log("Caught notification onClicked with ID:" + notificationId);
//Open a new tab with the desired URL:
browser.tabs.create({url:"http://www.google.com"});
});
My preference for something like this where a unique ID is possible is to provide a unique ID, that both identifies that the notification was displayed by my add-on and what the notification was. This allows the listener function to choose to only act on notifications which were displayed by my add-on and/or only a subset of those I display, or have different actions based on why I displayed the notification.
The reason you had trouble here in figuring out what was not working is probably because you did not reduce the issue to the minimum necessary to demonstrate the issue. In other words, it would have been a good idea to just try opening a new URL separately from the notifications onClicked listener and just try a console.log() within the listener.

map keyboard keys with mootools

I am looking to make the enter key behave exactly like the tab key on a form.
I am stuck on the fireEvent section.
var inputs = $$('input, textarea');
$each(inputs,function(el,i) {
el.addEvent('keypress',function(e) {
if(e.key == 'enter') {
e.stop();
el.fireEvent('keypress','tab');
}
});
});
How do I fire a keypress event with a specified key? Any help would be greatly appreciated.
this will work but it relies on dom order and not tabindex
var inputs = $$('input,textarea');
inputs.each(function(el,i){
el.addEvent('keypress',function(e) {
if(e.key == 'enter'){
e.stop();
var next = inputs[i+1];
if (next){
next.focus();
}
else {
// inputs[0].focus(); or form.submit() etc.
}
}
});
});
additionally, textarea enter capture? why, it's multiline... anyway, to do it at keyboard level, look at Syn. https://github.com/bitovi/syn
the above will fail with hidden elements (you can filter) and disabled elements etc. you get the idea, though - focus(). not sure what it will do on input[type=radio|checkbox|range] etc.
p.s. your code won't work because .fireEvent() will only call the bound event handler, not actually create the event for you.
Take a look at the class keyboard (MooTools More).
It can fire individual events for keys and provides methodology to disable and enable the listeners assigned to a Keyboard instance.
The manual has some examples how to work with this class, here's just a simple example how I implemented it in a similar situation:
var myKeyEv1 = new Keyboard({
defaultEventType: 'keydown'
});
myKeyEv1.addEvents({
'shift+h': myApp.help() // <- calls a function opening a help screen
});
Regarding the enter key in your example, you have to return false somewhere to prevent the enter-event from firing. Check out this SO post for more details.

Resources