Using JobQueue to continuously refresh a message - python-telegram-bot

I'm building a Telegram bot that uses ConversationHandler to prompt the user for a few parameters and settings about how the bot should behave. This information is stored in some global variables since it needs to be available and editable by different functions inside the program. Every global variable is a dictionary in which each user is associated with its own value. Here's an example:
language = {123456: 'English', 789012: 'Italian'}
where 123456 and 789012 are user ids obtained from update.message.from_user.id inside each function.
After all the required information has been received and stored, the bot should send a message containing a text fetched from a web page; the text on the web page is constantly refreshed, so I want the message to be edited every 60 seconds and updated with the new text, until the user sends the command /stop.
The first solution that came to my mind in order to achieve this was something like
info_message = bot.sendMessage(update.message.chat_id, text = "This message will be updated...")
...
def update_message(bot, update):
while True:
url = "http://example.com/etc/" + language[update.message.from_user.id]
result = requests.get(url).content
bot.editMessageText(result, chat_id = update.message.chat_id, message_id = info_message.message_id)
time.sleep(60)
Of course that wouldn't work at all, and it is a really bad idea. I found out that the JobQueue extension would be what I need. However, there is something I can't figure out.
With JobQueue I would have to set up a callback function for my job. In my case, the function would be
def update_message(bot, job):
url = "http://example.com/etc/" + language[update.message.from_user.id]
result = requests.get(url).content
bot.editMessageText(result, chat_id = update.message.chat_id, message_id = info_message.message_id)
and it would be called every 60 seconds. However this wouldn't work either. Indeed, the update parameter is needed inside the function in order to fetch the page according to the user settings and to send the message to the correct chat_id. I'd need to pass that parameter to the function along with bot, job, but that doesn't seem to be possible.
Otherwise I would have to make update a global variable, but I thought there must be a better solution. Any thoughts? Thanks.

I had the same issue. A little digging into the docs revealed that you can pass job objects a context parameter which can then be accessed by the callback function as job.context.
context (Optional[object]) – Additional data needed for the callback function. Can be accessed through job.context in the callback. Defaults to None
global language
language = {123456: 'English', 789012: 'Italian'}
j=updater.job_queue
context={"chat_id":456754, "from_user_id":123456, "message_id":111213}
update_job = job(update_message, 60, repeat=True, context=context)
j.put(update_job, next_t=0.0)
def update_message(bot, job):
global language
context=job.context
url = "http://example.com/etc/" + language[context["from_user_id"]]
result = requests.get(url).content
bot.editMessageText(result,
chat_id = context["chat_id"],
message_id = context["message_id"])

Related

RASA FormAction ActionExecutionRejection doesn’t re-prompt for missing slot

I am trying to implement a FormAction here, and I’ve overridden validate method.
Here is the code for the same:
def validate(self, dispatcher, tracker, domain):
logger.info("Validate of single entity called")
document_number = tracker.get_slot("document_number")
# Run regex on latest_message
extracted = re.findall(regexp, tracker.latest_message['text'])
document_array = []
for e in extracted:
document_array.append(e[0])
# generate set for needed things and
document_set = set(document_array)
document_array = list(document_set)
logger.info(document_set)
if len(document_set) > 0:
if document_number and len(document_number):
document_array = list(set(document_array + document_number))
return [SlotSet("document_number", document_array)]
else:
if document_number and len(document_number):
document_array = list(set(document_array + document_number))
return [SlotSet("document_number", document_array)]
else:
# Here it doesn't have previously set slot
# So Raise an error
raise ActionExecutionRejection(self.name(),
"Please provide document number")
So, ideally as per the docs, when ActionExecutionRejection occurs, it should utter a template with name utter_ask_{slotname} but it doesn’t trigger that action.
Here is my domain.yml templates
templates:
utter_greet:
- text: "Hi, hope you are having a good day! How can I help?"
utter_ask_document_number:
- text: "Please provide document number"
utter_help:
- text: "To find the document, please say the ID of a single document or multiple documents"
utter_goodbye:
- text: "Talk to you later!"
utter_thanks:
- text: "My pleasure."
The ActionExecutionRejection doesn't by default utter a template with the name utter_ask_{slotname}, but rather leaves the form logic to allow other policies (e.g. FallbackPolicy) to take action. The utter_ask_{slotname} is the default for the happy path in which it's trying to get a required slot for the first time. This default implementation of the action rejection is there in order to handle certain unhappy paths such as if a user decides they want to exit the flow by denying, or take a detour by chatting, etc.
If you want to implement the template to re-ask for the required slot using the utterance, you could replace the ActionExecutionRejection with dispatcher.utter_template(<desired template name>, tracker). However, this will leave you with no way to exit the form action without validation -- I don't know what your intents are, but perhaps you want to also incorporate some logic based on the intent (i.e. if it's something like "deny", let the ActionExecutionRejection happen so it can exit, it it's an "enter data" type of intent make sure it asks again).

how to store the user input in a parameter in dialogflow

I have a Intent where I have a parameter called age and its value I want it to be the user input.
And in the response I would like to responde with the same input as user given ex:
User Input: Hello, haw are you.
Bot: You said: Hello, haw are you.
So I would need the user input first to store in a parameter and from the Text Response section I can call the parameter but I just don't know how to catch the user input at this moment!
So Text response would be like: You said: $input.
Assuming that you are using Javascript, you can find many useful details here.
Depending on the way you get the input, the solution is totally different.
For instance, in this link you get the input via a click (this.button.addEventListener("click", this.handleClick.bind(this))) or in the following code you get the input as a query.
import {ApiAiClient} from "api-ai-javascript";
import {IRequestOptions, IServerResponse, ApiAiConstants} from "api-ai-javascript/ApiAiClient"
const client = new ApiAiClient({accessToken: 'YOUR_ACCESS_TOKEN'})
queryInput.addEventListener("keydown", queryInputKeyDown);
.textRequest('Hello!')
.then((response) => {console.log(queryInput.value);})
Thus, in the above example, you can use:
value = queryInput.value
you can try it in Inline Editor Fulfillment
function userInput(agent) {
let user_input = agent.query;
//test it
agent.add(user_input);
}

web audio filter response

I have a simple filter.
var filter = ctx.createBiquadFilter();
filter.type = 'highpass';
filter.frequency.setValueAtTime(10,ctx.currentTime);
I would like to see its frequency response using getFrequencyResponse
window.setInterval(function() {
var frequencyHz = new Float32Array(1),
magResponse = new Float32Array(1),
phaseResponse = new Float32Array(1);
frequencyHz[0] = 10;
filter.getFrequencyResponse(frequencyHz,magResponse,phaseResponse);
console.log(magResponse);
},100);
I expect to see [0.9565200805664062] which is the correct response for 10Hz, but instead I see [0.0008162903832271695] which is the response for 350Hz, the default frequency value.
I can only get a sensible response if I manually set the value, whereas if I use param methods such as setValueAtTime, the filter response ignores them and spits out the default. In other words, getFrequencyResponse seems to only work if the filter values are set manually, preventing filter analysis when the values are set by automation. If this is true, this seems like more than a small problem with the api.
Someone please try something near to this, and if it works (doubtful) please post the code.
Actually, no, it should be taking effect, but because you're causing an instantaneous value change at the NEXT processing block (that's what ".currentTime" means), that change has not yet been processed - so you're seeing the default value. You should see, for example, getFrequencyResponse changes over time for a setLinearRamp.

Grails session variable null

My app has an expensive service method, results of which must be 1) checked for errors and 2) presented to a Java applet via a URL (i.e. as opposed to a JavaScript variable). The method result is a string, and the applet is only capable of loading data from a file or URL.
I tried to deal with the problem using a session variable:
def action1 = {
def input = params['input']
def result = expensiveServiceMethod( input )
def failed = result == null
session['result'] = result
render( view:'view1', model:[failed:failed] )
}
def action2 = {
def result = session['result']
render( result )
}
Then, in view1 the applet is conditionally displayed depending on the failure status, and the results are accessed by the applet via the action2 URL.
Unfortunately, result in action2 is coming up null. I've verified that result is not null in action1. Am I doing it wrong?
Note
I would have used flash instead, but there are additional requests made in order to initialize the applet.
Applets aren't able to track session cookies their self. So when your applet sends second request to action2 - it does't send session cookie back to server. Hence for server its like a brand new session, any thing you set in session during action1 won't be available in action2. You will have to track cookies in your applet and send them back to server when making calls.
I have never done it, but I think you may use Apache commons http client on your client side (applet) - it has support for tracking cookies
See this question -

Ruby script for posting comments

I have been trying to write a script that may help me to comment from command line.(The sole reason why I want to do this is its vacation time here and I want to kill time).
I often visit and post on this site.So I am starting with this site only.
For example to comment on this post I used the following script
require "uri"
require 'net/http'
def comment()
response = Net::HTTP.post_form(URI.parse("http://www.geeksforgeeks.org/wp-comments-post.php"),{'author'=>"pikachu",'email'=>"saurabh8c#gmail.com",'url'=>"geekinessthecoolway.blogspot.com",'submit'=>"Have Your Say",'comment_post_ID'=>"18215",'comment_parent'=>"0",'akismet_comment_nonce'=>"70e83407c8",'bb2_screener_'=>"1330701851 117.199.148.101",'comment'=>"How can we generalize this for a n-ary tree?"})
return response.body
end
puts comment()
Obviously the values were not hardcoded but for sake of clearity and maintaining the objective of the post i am hardcoding them.
Beside the regular fields that appear on the form,the values for the hidden fields i found out from wireshark when i posted a comment the normal way.I can't figure out what I am missing?May be some js event?
Edit:
As few people suggested using mechanize I switched to python.Now my updated code looks like:
import sys
import mechanize
uri = "http://www.geeksforgeeks.org/"
request = mechanize.Request(mechanize.urljoin(uri, "archives/18215"))
response = mechanize.urlopen(request)
forms = mechanize.ParseResponse(response, backwards_compat=False)
response.close()
form=forms[0]
print form
control = form.find_control("comment")
#control=form.find_control("bb2_screener")
print control.disabled
# ...or readonly
print control.readonly
# readonly and disabled attributes can be assigned to
#control.disabled = False
form.set_all_readonly(False)
form["author"]="Bulbasaur"
form["email"]="ashKetchup#gmail.com"
form["url"]="9gag.com"
form["comment"]="Y u no put a captcha?"
form["submit"]="Have Your Say"
form["comment_post_ID"]="18215"
form["comment_parent"]="0"
form["akismet_comment_nonce"]="d48e588090"
#form["bb2_screener_"]="1330787192 117.199.144.174"
request2 = form.click()
print request2
try:
response2 = mechanize.urlopen(request2)
except mechanize.HTTPError, response2:
pass
# headers
for name, value in response2.info().items():
if name != "date":
print "%s: %s" % (name.title(), value)
print response2.read() # body
response2.close()
Now the server returns me this.On going through the html code of the original page i found out there is one more field bb2_screener that i need to fill if I want to pretend like a browser to the server.But the problem is the field is not written inside the tag so mechanize won't treat it as a field.
Assuming you have all the params correct, you're still missing the session information that the site stores in a cookie. Consider using something like mechanize, that'll deal with the cookies for you. It's also more natural in that you tell it which fields to fill in with which data. If that still doesn't work, you can always use a jackhammer like selenium, but then technically you're using a browser.

Resources