Take Dialogflow intent and query Firestore - python

My chatbot has been created in Dialogflow and I am now trying to access it from Python, to take user inputs and display outputs in the GUI (think a basic chatbot gui).
I have connected my Python environment to Dialogflow and to firestore,
Here is the code that detects intents;
#Detection of Dialogflow intents, project and input.
def detect_intent_texts(project_id, session_id, texts, language_code):
#Returns the result of detect intent with texts as inputs, later can implement same `session_id` between requests allows continuation of the conversaion.
import dialogflow_v2 as dialogflow
session_client = dialogflow.SessionsClient()
session = session_client.session_path(project_id, session_id)
#To ensure session path is correct - print('Session path: {}\n'.format(session))
for text in texts:
text_input = dialogflow.types.TextInput(text=text, language_code=language_code)
query_input = dialogflow.types.QueryInput(text=text_input)
response = session_client.detect_intent(session=session, query_input=query_input)
print ('Chatbot:{}\n'.format(response.query_result.fulfillment_text))
detect_intent_texts("chat-8","abc",["Hey"],"en-us")
I need to somehow say if THIS intent is triggered, get something from the db and display to user.
UPDATE
My current code in full, everything is looking right to me but it's throwing an error I don't understand, thanks to Sid8491 for the help so far.
in short, my issue is, my previous code allowed me to type something and the chatbot respond, it was all in the console but it worked... The new code is supposed to allow me to say "When THIS intent is triggered, do THIS"
import os, json
import sys
import dialogflow
from dialogflow_v2beta1 import *
import firebase_admin
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
from firebase_admin import firestore
from firebase_admin import credentials
import requests.packages.urllib3
from Tkinter import *
from dialogflow_v2beta1 import agents_client
import Tkinter as tk
result = None
window = Tk()
def Response():
#no need to use global here
result = myText.get()
displayText.configure(state='normal')
displayText.insert(END, "User:"+ result + '\n')
displayText.configure(state='disabled')
#Creating the GUI
myText = tk.StringVar()
window.resizable(False, False)
window.title("Chatbot")
window.geometry('400x400')
User_Input = tk.Entry(window, textvariable=myText, width=50).place(x=20, y=350)
subButton = tk.Button(window, text="Send", command=Response).place(x =350, y=350)
displayText = Text(window, height=20, width=40)
displayText.pack()
scroll = Scrollbar(window, command=displayText).pack(side=RIGHT)
window.mainloop()
#Initialize the firebase admin SDK
cred = credentials.Certificate('./file.json')
default_app = firebase_admin.initialize_app(cred)
db = firestore.client()
def getCourse():
doc_ref = db.collection(u"Course_Information").document(u"CourseTypes")
try:
doc = doc_ref.get()
return 'Document data: {}'.format(doc.to_dict())
except google.cloud.exceptions.NotFound:
return 'Not found'
def detect_intent_text(project_id, session_id, text, language_code):
GOOGLE_APPLICATION_CREDENTIALS=".chat-8.json"
session_client = dialogflow.SessionsClient(GOOGLE_APPLICATION_CREDENTIALS)
session = session_client.session_path(project_id, session_id)
text_input = dialogflow.types.TextInput(
text=text, language_code=language_code)
query_input = dialogflow.types.QueryInput(text=text_input)
response = session_client.detect_intent(
session=session, query_input=query_input)
queryText = [myText.get()]
res = detect_intent_text('chat-8', 'session-test', queryText, 'en')
intentName = res['query_result']['intent']['display_name']
if intentName == 'CourseEnquiry':
reponse = getCourse()
print json.dumps({
'fulfillmentText': reponse,
})
elif intentName == 'Greetings':
print "Yo"
detect_intent_texts("chat-8","abc", queryText,"en-us")
But I get this error:
C:\Users\chat\PycharmProjects\Chatbot\venv\Scripts\python.exe C:/Users/chat/PycharmProjects/Chatbot/venv/Chatbot.py
Traceback (most recent call last):
File "C:/Users/chat/PycharmProjects/Chatbot/venv/Chatbot.py", line 65, in <module>
res = detect_intent_text('chat-8', 'session-test', queryText, 'en')
File "C:/Users/chat/PycharmProjects/Chatbot/venv/Chatbot.py", line 51, in detect_intent_text
session_client = dialogflow.SessionsClient(GOOGLE_APPLICATION_CREDENTIALS)
File "C:\Users\chat\PycharmProjects\Chatbot\venv\lib\site-packages\dialogflow_v2\gapic\sessions_client.py", line 109, in __init__
self.sessions_stub = (session_pb2.SessionsStub(channel))
File "C:\Users\chat\PycharmProjects\Chatbot\venv\lib\site-packages\dialogflow_v2\proto\session_pb2.py", line 1248, in __init__
self.DetectIntent = channel.unary_unary(
AttributeError: 'str' object has no attribute 'unary_unary'
Process finished with exit code 1

Yes, I think you are on the right track.
You need to extract intentName or actionaName from the response you got from the dialogFlow and call your corresponding functions and then send the response back to user.
res = detect_intent_texts("chat-8","abc",["Hey"],"en-us")
action = res['queryResult']['action']
if action == 'getSomethingFromDb':
reponse = someFunction(req)
return json.dumps({
'fulfillmentText': reponse,
})
elif action == 'somethingElse':
....
If you want it to do using intentName instead of actionName then you can extract intentName like below
intentName = res['query_result']['intent']['display_name']
EDIT 1:
Example -
import dialogflow
import os, json
def getCourse():
doc_ref = db.collection(u"Course_Information").document(u"CourseTypes")
try:
doc = doc_ref.get()
return 'Document data: {}'.format(doc.to_dict())
except google.cloud.exceptions.NotFound:
return 'Not found'
def detect_intent_text(project_id, session_id, text, language_code):
GOOGLE_APPLICATION_CREDENTIALS="C:\\pyth_to_...\\cred.json"
session_client = dialogflow.SessionsClient(GOOGLE_APPLICATION_CREDENTIALS)
session = session_client.session_path(project_id, session_id)
text_input = dialogflow.types.TextInput(
text=text, language_code=language_code)
query_input = dialogflow.types.QueryInput(text=text_input)
response = session_client.detect_intent(
session=session, query_input=query_input)
queryText = 'get courses of Python' # you will call some function to get text from your app
res = detect_intent_text('project_1234', 'session-test', queryText, 'en')
intentName = res['query_result']['intent']['display_name']
if intentName == 'getCourse':
reponse = getCourse()
return json.dumps({
'fulfillmentText': reponse,
})
Try above example and change according to your needs of app. My suggestion is to first get DialogFlow working without app, then integrate it with the app. Otherwise you won't be able to understand whether problem is happening in DialogFlow or your app.
Hope it helps.

Related

Need to pass variable "update" to method news()

I'm trying to create a python telegram bot that send random news every x time, and it works when i give the method news() the chat_id statically directly on the method, I tried passing it directly on the method start(), that execute a run_repeating job like that:
def start():
context.job_queue.run_repeating(news(update=update, context=context), interval=newsTimer, first=1)
but after you run the command for the first time (and works) it trows a lot of errors.
Instead I hardcoded the chat_id inside the news() method.
Anyone has any hint on how could I pass the variable update to the news() method?
Here's my full code:
from dotenv import load_dotenv
import telegram.ext
import requests
import datetime
import random
import json
import os
load_dotenv()
token = os.getenv('TOKEN')
api = os.getenv('API')
languages = "en"
newsTimer = 600
query = "world"
pageSize = "100"
yesterday = str(datetime.datetime.now() - datetime.timedelta(days=1))
today = str(datetime.datetime.now())
api_url = "https://newsapi.org/v2/everything?q=" + query + "&from=" + yesterday + "&to=" + today + "&sortBy=popularity&pageSize=+"+ pageSize +"&apiKey=" + api
def start(update, context):
context.job_queue.run_repeating(news, interval=newsTimer, first=1)
def help(update, context):
update.message.reply_text("""
Available commands:
/start - start bot
/help - help
""")
def news(context):
response = requests.get(api_url)
json_data = json.loads(response.text)
i = random.randint(0, 99)
title = json_data['articles'][i]['title']
image = json_data['articles'][i]['urlToImage']
description = json_data['articles'][i]['description']
author = json_data['articles'][i]['author']
article = json_data['articles'][i]
if title is None:
title = "No title"
if image is None:
image = "https://www.alfasolare.ru/a_solar_restyle/wp-content/themes/consultix/images/no-image-found-360x260.png"
if description is None:
description = "No Content"
if author is None:
author = "No Author"
if article is None:
i = random.randint(0, len(json_data['articles']))
caption = "<a>" + title + "\n" + description + "\n Author: " + author + "</a>"
chat_id = "XXXXXXXXX" #my own chat id
context.bot.send_photo(
chat_id=chat_id,
photo=image,
caption=caption,
parse_mode=telegram.ParseMode.HTML
)
if __name__ == '__main__':
updater = telegram.ext.Updater(token, use_context=True)
dispatcher = updater.dispatcher
dispatcher.add_handler(telegram.ext.CommandHandler('start', start))
dispatcher.add_handler(telegram.ext.CommandHandler('help', help))
if updater.start_polling():
print("Bot started successfully!")
if updater.idle():
print("Bot stopped")
context.job_queue.run_repeating(news(update=update, context=context), interval=newsTimer, first=1)
this can't work since the callback argument of JobQueue.run_* must be a function. However, news(update=update, context=context) is the return value of that function call, which is None as news doesn't return anything.
To pass additional data to the job callback, you can use the context argument of JobQueue.run_*. This is also explained in PTBs wiki page on JobQueue and showcased in the timerbot.py example.
Disclaimer: I'm currently the maintainer of python-telegram-bot

Obtain Intents, Entities and the training data from Dialogflow in the python environment

I want to know if there is some way through which I can obtain all the Intents(its corresponding questions), Entities and the training data(I defined in Google Dialogflow) programmatically using python.
Following is the code(works fine) through which i get a response from dialogflow.
GOOGLE_APPLICATION_CREDENTIALS = '###'
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] =
GOOGLE_APPLICATION_CREDENTIALS
def dialogflow_api_response(text):
session_client = dialogflow.SessionsClient()
session = session_client.session_path('###', '##')
text_input = dialogflow.types.TextInput(text=text, language_code='en')
query_input = dialogflow.types.QueryInput(text=text_input)
response = session_client.detect_intent(session=session, query_input=query_input)
jsonObj = MessageToJson(response)
x = json.loads(jsonObj)
If there is, please point me in the right direction.
Thank you
Yes it is possible to get all the intents, entities, unserEntities and contexts from Dialogflow agent using Dilogflow API as JSON object. Here's the code to get the list of intents:
def list_intents(project_id):
import dialogflow_v2 as dialogflow
intents_client = dialogflow.IntentsClient()
parent = intents_client.project_agent_path(project_id)
intents = intents_client.list_intents(parent)
for intent in intents:
print('=' * 20)
print('Intent name: {}'.format(intent.name))
print('Intent display_name: {}'.format(intent.display_name))
print('Action: {}\n'.format(intent.action))
print('Root followup intent: {}'.format(
intent.root_followup_intent_name))
print('Parent followup intent: {}\n'.format(
intent.parent_followup_intent_name))
print('Input contexts:')
for input_context_name in intent.input_context_names:
print('\tName: {}'.format(input_context_name))
print('Output contexts:')
for output_context in intent.output_contexts:
print('\tName: {}'.format(output_context.name))
for training_phrase in intent.training_phrases:
train_phrase = training_phrase.parts[0].text
print(train_phrase)
For further information you can refer to DF official gitHub here

How to detect changes in firebase child with python?

I have some troubles with this application. What I need is that If I detect a change in the database (FIREBASE) particularly in 'sala' and 'ventilacion' nodes the function do what it have to do. If there isn't any change in the database it would not do nothing. I am using python and pyrebase library. Here is the code. Thank you very much for you help.
import pyrebase
import serial
import time
config = {
#firebase configurations
}
firebase = pyrebase.initialize_app(config)
db = firebase.database()
def ReconfiguracionFabrica():
ser.write('AT')
time.sleep(0.2)
ser.write('AT+RENEW')
time.sleep(0.3)
def ConfiguracionMaster():
time.sleep(0.5)
ser.write('AT+IMME1')
time.sleep(0.350)
ser.write('AT+ROLE1')
time.sleep(0.2)
ser = serial.Serial(port="/dev/ttyAMA0", baudrate=9600, timeout=1)
ReconfiguracionFabrica()
time.sleep(0.1)
ConfiguracionMaster()
time.sleep(0.1)
print "********** INICIO *************"
ser.flushInput()
contador = 0
prender = ''
ventilacion1 = ''
checkeo = ''
while True:
#if db.child("sala").: # It is the line where would be the conditional that allows me to detect any change only in the sala's node.
salidaLed1 = db.child("sala").get()
ser.write('AT')
time.sleep(0.1)
ser.write('AT+CON508CB16A7014')
time.sleep(0.1)
if salidaLed1.val() == True:
prender = ";"
if salidaLed1.val() == False:
prender = ","
ser.write('luz: %s \n' %(prender))
print ('luz: %s \n' %(prender))
time.sleep(1)
ser.read(checkeo)
if checkeo == 'j':
ReconfiguracionFabrica()
time.sleep(0.1)
ConfiguracionMaster()
Question: How to detect changes in firebase child
Note: All Examples use Public Access
Setup Example Data and verify it's readable.
This hase to be done once!
temperature_c = 30
data = {'date':time.strftime('%Y-%m-%d'),
'time':time.strftime('%H:%M:%S'),
'temperature':temperature_c}
db.child('public').child('Device_1').set(data)
response = db.child('public').child('Device_1').get()
print(response.val())
Create First Script doing Updates:
for t in [25, 26, 27, 28, 29, 30, 31, 32, 33, 35]:
temperature_c = t
data = {'date':time.strftime('%Y-%m-%d'),
'time':time.strftime('%H:%M:%S'),
'temperature':temperature_c}
db.child('public').child('Device_1').update(data)
time.sleep(60)
Create Second Script with Stream Handler
def stream_handler(message):
print('event={m[event]}; path={m[path]}; data={m[data]}'
.format(m=message))
my_stream =db.child('public').child('Device_1').stream(stream_handler)
# Run Stream Handler forever
while True:
data = input("[{}] Type exit to disconnect: ".format('?'))
if data.strip().lower() == 'exit':
print('Stop Stream Handler')
if my_stream: my_stream.close()
break
Run Stream Handler Script:
Response Output from def stream_handler after startup (Initial Data):
event="put"; path=/; data={'Device_1': {'temperature': 30, 'time': '13:34:24', 'date': '2017-07-20'}}
Run Updater Script:
Watch Output from Stream Handler Script
Response Output from def stream_handler after First Update Data:
event=patch; path=/Device_1; data={'temperature': 25, 'time': '13:49:12'}
Tested with Python: 3.4.2
Pyrebase
streaming
You can listen to live changes to your data with the stream() method.
def stream_handler(message):
print(message["event"]) # put
print(message["path"]) # /-K7yGTTEp7O549EzTYtI
print(message["data"]) # {'title': 'Pyrebase', "body": "etc..."}
my_stream = db.child("posts").stream(stream_handler)
You should at least handle put and patch events. Refer to "Streaming from the REST API" for details.
I know this post is 2 years old but hope this helps. Try using firebase_admin module.
Use this command - pip install firebase-admin
I too had a requirement where I needed to check for changes made to the Firebase database. I referred here
Following is a sample code based on your question which you can refer from and try it out.
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
cred = credentials.Certificate("path/to/serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
'databaseURL': 'https://example.firebaseio.com',
'databaseAuthVariableOverride': None
})
def ignore_first_call(fn):
called = False
def wrapper(*args, **kwargs):
nonlocal called
if called:
return fn(*args, **kwargs)
else:
called = True
return None
return wrapper
#ignore_first_call
def listener(event):
print(event.event_type) # can be 'put' or 'patch'
print(event.path) # relative to the reference, it seems
print(event.data) # new data at /reference/event.path. None if deleted
node = str(event.path).split('/')[-2] #you can slice the path according to your requirement
property = str(event.path).split('/')[-1]
value = event.data
if (node=='sala'):
#do something
elif (node=='ventilacion'):
#do something
else:
#do something else
db.reference('/').listen(listener)
I was working on the same thing so according to current updates on pyrebase and learning from above posted answers, I got this running perfectly.(Please make sure your python is upgraded from python2 to python3 for running pyrebase and firebase-admin)
import firebase_admin
import pyrebase
from firebase_admin import credentials
config = {
"apiKey": "",
"authDomain": "",
"databaseURL": "",
"projectId": "",
"storageBucket": "",
"serviceAccount": "path to the service account json file you downloaded",
"messagingSenderId": "",
"appId": "",
"measurementId": ""
}
firebase = pyrebase.initialize_app(config)
storage = firebase.storage()
cred = credentials.Certificate("path to downloaded json file")
firebase_admin.initialize_app(cred, {
"databaseURL": "same as config",
"databaseAuthVariableOverride": None
})
db = firebase.database()
def ignore_first_call(fn):
called = False
def wrapper(*args, **kwargs):
nonlocal called
if called:
return fn(*args, **kwargs)
else:
called = True
return None
return wrapper
def stream_handler(message):
ab = str(1)
all_videos = storage.child("videos/").list_files() #node where files are
path_on_local = "local path to save the downloads"
print(message["event"]) # put
print(message["path"]) # /-K7yGTTEp7O549EzTYtI
print(message["data"]) # {'title': 'Pyrebase', "body": "etc..."}
node = str(message["path"]).split('/')[-2]
property = str(message["path"]).split('/')[-1]
value = message["data"]
if (message["event"] == "put"):
for videos in all_videos:
try:
print(videos.name)
z = storage.child(videos.name).get_url(None)
storage.child(videos.name).download(path_on_local + "/" + ab + ".mp4")
x = int(ab)
ab = str(x + 1)
except:
print('Download Failed')
else:
print("error")
my_stream = db.child("videos").stream(stream_handler)

How to restart telegram bot in Python (with initial state of variables) for new client?

this is my first question here. I try to rewrite telegram bot in Python. And I cant solve one problem. When I test my bot by myself everything fine, but if another user connect with my bot script doesn`t start for him with initial state of variables. For example, in this code script increment x variable when user send "1". But for new user x already not null. Please help me to fix it.
import StringIO
import json
import logging
import random
import urllib
import urllib2
# for sending images
from PIL import Image
import multipart
# standard app engine imports
from google.appengine.api import urlfetch
from google.appengine.ext import ndb
import webapp2
TOKEN = 'TOKEN HERE'
BASE_URL = 'https://api.telegram.org/bot' + TOKEN + '/'
x = 0
# ================================
class EnableStatus(ndb.Model):
# key name: str(chat_id)
enabled = ndb.BooleanProperty(indexed=False, default=False)
# ================================
def setEnabled(chat_id, yes):
es = EnableStatus.get_or_insert(str(chat_id))
es.enabled = yes
es.put()
def getEnabled(chat_id):
es = EnableStatus.get_by_id(str(chat_id))
if es:
return es.enabled
return False
# ================================
class MeHandler(webapp2.RequestHandler):
def get(self):
urlfetch.set_default_fetch_deadline(60)
self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'getMe'))))
class GetUpdatesHandler(webapp2.RequestHandler):
def get(self):
urlfetch.set_default_fetch_deadline(60)
self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'getUpdates'))))
class SetWebhookHandler(webapp2.RequestHandler):
def get(self):
urlfetch.set_default_fetch_deadline(60)
url = self.request.get('url')
if url:
self.response.write(json.dumps(json.load(urllib2.urlopen(BASE_URL + 'setWebhook', urllib.urlencode({'url': url})))))
class WebhookHandler(webapp2.RequestHandler):
def post(self):
urlfetch.set_default_fetch_deadline(60)
body = json.loads(self.request.body)
logging.info('request body:')
logging.info(body)
self.response.write(json.dumps(body))
update_id = body['update_id']
message = body['message']
message_id = message.get('message_id')
date = message.get('date')
text = message.get('text')
fr = message.get('from')
chat = message['chat']
chat_id = chat['id']
if not text:
logging.info('no text')
return
def reply(msg=None, img=None):
if msg:
resp = urllib2.urlopen(BASE_URL + 'sendMessage', urllib.urlencode({
'chat_id': str(chat_id),
'text': msg.encode('utf-8'),
'disable_web_page_preview': 'true',
#'reply_to_message_id': str(message_id),
})).read()
elif img:
resp = multipart.post_multipart(BASE_URL + 'sendPhoto', [
('chat_id', str(chat_id)),
#('reply_to_message_id', str(message_id)),
], [
('photo', 'image.jpg', img),
])
else:
logging.error('no msg or img specified')
resp = None
logging.info('send response:')
logging.info(resp)
if text.startswith('/'):
if text == '/start':
reply('Bot start')
setEnabled(chat_id, True)
elif text == '/stop':
reply('Bot disabled')
setEnabled(chat_id, False)
else:
reply('What command?')
elif '1' in text:
global x
x = x + 1
reply(str(x))
else:
if getEnabled(chat_id):
reply('?')
else:
logging.info('not enabled for chat_id {}'.format(chat_id))
app = webapp2.WSGIApplication([
('/me', MeHandler),
('/updates', GetUpdatesHandler),
('/set_webhook', SetWebhookHandler),
('/webhook', WebhookHandler),
], debug=True)
Instead of variable x, you may want to use python dictionary like this -
x = {}
and then use it like this -
key = str(chat_id)
if key in x:
x[key] += 1
else:
x[key] = 1

modifying user email using jenkins api with python

Is there a way to modify user details (email in particular) using jenkins api?
Posting a modified json file to {root}/user/{username}/api/json does not seem to modify the underlying data:
import urllib2
import json
root = 'your_url'
username = 'your_username'
user_url = root +'/user/{username}/api/json'.format(username=username)
orig_d = json.loads((urllib2.urlopen(user_url).read()))
d = dict(orig_d)
d['fullName'] = 'XXXXX'
json_data = json.dumps(d)
request = urllib2.Request(user_url)
request.add_header('Content-type', 'application/json')
new_d = json.loads(urllib2.urlopen(request, json_data).read())
print new_d, '\n', orig_d
assert orig_d!=new_d, 'They are equal!'
After looking at jenkins source code model/User.java it looks like the only "POST" configuration that is supported on User model is done through doConfigSubmit() member function. So I eneded up faking web POST form at jenkins_url/user/{username}/configSubmit. The following snippet worked for me:
import urllib2, urllib
import json
root = jenkins_url
username = username
user_url = root +'/user/{username}/api/json'.format(username=username)
config_url = root + '/user/{username}/configSubmit'.format(username=username)
d = json.loads((urllib2.urlopen(user_url).read()))
fullname = d['fullName']
description = d['description']
new_email = 'new_user_email'
post_d = {"userProperty1": {"address": new_email}}
request = urllib2.Request(config_url)
values = {'json': json.dumps(post_d),
'fullName': fullname,
'email.address': new_email,
'description': description}
data = urllib.urlencode(values)
response = urllib2.urlopen(request, data).read()

Categories