Channel API Presence not triggering - python

I'm trying to handle disconnect / connect states using Presence in the Channel API.
Here are some of my code.
app.yaml
handlers:
- url: /(.*\.(gif|png|jpg|ico|js|css))
static_files: \1
upload: (.*\.(gif|png|jpg|ico|js|css))
- url: .*
script: main.py
inbound_services:
- channel_presence
main.py
class onConnect(webapp.RequestHandler):
def post(self):
for user in users:
users = User.all().fetch(1000)
client = client_id = self.request.get('from')
channel.send_message(user.channel,' connected');
class onDisconnect(webapp.RequestHandler):
def post(self):
Mainpage()
for user in users:
users = User.all().fetch(1000)
client = client_id = self.request.get('from')
channel.send_message(user.channel, ' disconnected');
application = webapp.WSGIApplication(
[('/', MainPage),
('/_ah/channel/connected/',onConnect),
('/_ah/channel/disconnected/',onDisconnect),
('/chat',handleChat)],
debug=True)
Javascript
<script>
openChannel = function(){
var token = '{{ token }}';
var channel = new goog.appengine.Channel(token);
var handler = {
'onopen': onOpened,
'onmessage': onMessage,
'onerror': function() {},
'onclose': function() {}
};
var socket = channel.open(handler);
socket.onopen = onOpened;
socket.onmessage = onMessage;
var chat = document.getElementById('chatinput');
chat.onkeyup = function(e){
if(e.keyCode == 13){
sendChat(this.value);
this.value = '';
}
}
}
sendMessage = function(path, opt_param) {
if (opt_param) {
path += '?' + opt_param;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', path, true);
xhr.send();
};
onOpened = function(){
console.log('Channel Opened');
var chatlog = document.getElementById('chatlog');
var msg = document.createElement('div');
msg.innerHTML = 'Channel Opened';
chatlog.appendChild(msg);
sendMessage('/chat','m='+'A User Joined.');
}
onMessage = function(m){
console.log('Message Recieved');
var chatlog = document.getElementById('chatlog');
var msg = document.createElement('div');
var d = new Date();
msg.innerHTML = d.toLocaleTimeString() + ': ' + m.data;
chatlog.appendChild(msg);
}
sendChat = function(msg){
console.log(msg);
sendMessage('/chat','m='+msg);
}
openChannel();
</script>
Using this code, connnect and disconnect is not triggering when a user closes their browser or whatever.
Are there anything wrong with this code?

Yes, route list is wrong. Put ('/', MainPage) in the end of the routes list. From webapp2 URI routing guide:
When the application receives a request, it tries to match each one in order until one matches, and then call the corresponding handler.

Related

Having Twilio call 1 number, get the call status and then proceeding to call the next number on Apps Script

I have a list of phone numbers to call on google spreadsheet and I have coded up on apps script to call the numbers.
However, I would like twilio to be able to call the first number, get its call status (completed, busy, failed etc), update it on the spreadsheet and then proceed to call the second number.
This is how I want my spreadsheet output to look like.
Currently, I am unable to get the call status from twilio using Apps Script. Would love to have some feedback on what I'm doing wrongly.
Thanks.
This is my Apps Script code
// Enter your Twilio account information here.
var TWILIO_ACCOUNT_SID = '';
var TWILIO_NUMBER = '';
var TWILIO_AUTH_TOKEN = '';
// Other variables
var STARTROW = 2
var STARTCOL = 1
// Creates the Call button on the menu bar
function onOpen() {
// To learn about custom menus, please read:
// https://developers.google.com/apps-script/guides/menus
var ui = SpreadsheetApp.getUi();
ui.createMenu('Call')
.addItem('Call Selected', 'confirmCall')
.addToUi();
};
// Ensuring variables are entered
function checkSetup() {
if (TWILIO_ACCOUNT_SID && TWILIO_AUTH_TOKEN) {
return true;
} else {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Please check your Twilio account details in AppScript');
return false;
}
}
// Confirmation Pop-up alert
// Asks user if they have updated ngrok website
function confirmCall(){
// Display a dialog box with a message and "Yes" and "No" buttons.
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Is the Phone Number the last column and have you updated the ngrok website in the Apps Script?', ui.ButtonSet.YES_NO);
// Process the user's response.
if (response == ui.Button.YES) {
Logger.log('The user clicked "Yes."');
// Starts making the calls
all_selected();
} else {
ui.alert('Please go to the Tools menu bar > Script Editor to update. \n If unsure, please check manual.');
Logger.log('The user clicked "No" or the dialog\'s close button.');
}
}
// Intiates calls to the selected users
function all_selected() {
var sheet = SpreadsheetApp.getActiveSheet();
var ui = SpreadsheetApp.getUi();
var totalRows = sheet.getLastRow();
var numRows = sheet.getLastRow() - STARTROW + 1;
var numColumns = sheet.getLastColumn();
var nextColumn = numColumns + 1;
// Create new column
sheet.getRange(1,nextColumn).setValue("Response")
if (!checkSetup) {
return
}
if (numRows <= 0) {
Logger.log("No one to call");
var response = ui.alert('No calls to make');
return;
}
for (var i = STARTROW; i <= (totalRows); i++) {
var number = sheet.getRange(i,numColumns).getValue();
var response = makeCall(number);
//Browser.msgBox(response);
// sheet.getRange(i,nextColumn + 1).setValue(response["status"])
// If there is an error, update cell value to Nil
if(response.code){
sheet.getRange(i,nextColumn).setValue("Nil")
Logger.log("Error has occured.");
}
}
}
// Calls a (ONE) person
function makeCall(to) {
var call_url = "https://api.twilio.com/2010-04-01/Accounts/" + TWILIO_ACCOUNT_SID + "/Calls.json";
var payload = {
"To": "+" + String(to),
"From" : TWILIO_NUMBER,
"Url": "http://821f72d16be7.ngrok.io/" + "voice",
"status_callback" : 'http://821f72d16be7.ngrok.io/' + "status",
"Method": "GET",
"status_callback_event" : ['queued', 'initiated'],
"status_callback_method" : 'POST'
};
var options = {
"method" : "post",
"payload" : payload
};
options.headers = {
"Authorization" : "Basic " + Utilities.base64Encode(TWILIO_ACCOUNT_SID + ":" + TWILIO_AUTH_TOKEN)
};
var response = UrlFetchApp.fetch(call_url, options);
return JSON.parse(response);
}
This is my python Colab code
from flask import Flask, request, session
from flask_ngrok import run_with_ngrok
import os
from twilio.rest import Client
from twilio import twiml
from twilio.twiml.voice_response import VoiceResponse, Gather
from google.colab import auth
import gspread
from oauth2client.client import GoogleCredentials
# Change this if using a new sheet
sheet_id = ""
# Accesses the google spreadsheet
auth.authenticate_user()
gc = gspread.authorize(GoogleCredentials.get_application_default())
# Opens the spreadsheet(****First sheet****)
worksheet = gc.open_by_key(sheet_id).sheet1
# Gets the number of columns in the current sheet
num_col = len(worksheet.get_all_values()[0])
col = num_col + 1
####################################################################
TWILIO_ACCOUNT_SID = ''
TWILIO_NUMBER = ''
TWILIO_AUTH_TOKEN = ''
client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
# Makes connection using FLASK
app = Flask(__name__)
run_with_ngrok(app)
#app.route("/status", methods=['GET', 'POST'])
def status():
# Need to get the number of rows in that column
row = len(worksheet.col_values(num_col))
worksheet.update_cell(row, col+1, request.values['Status'])
# client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
# record = client.calls.list(limit=1)
# status = record.status
# if status == 'failed':
# worksheet.update_cell(row, col, "failed")
#app.route("/voice", methods=['GET', 'POST'])
def voice():
"""Respond to incoming phone calls with a menu of options"""
# Start our TwiML response
resp = VoiceResponse()
# Start our <Gather> verb
gather = Gather(num_digits=1, action='/gather')
gather.say('If you are interested in our service, press 1. If not, press 2.')
resp.append(gather)
# If the user doesn't select an option, redirect them into a loop
resp.redirect('/voice')
return str(resp)
#app.route('/gather', methods=['GET', 'POST'])
def gather():
"""Processes results from the <Gather> prompt in /voice"""
# Start our TwiML response
resp = VoiceResponse()
# Need to get the number of rows in that column
row = len(worksheet.col_values(num_col))
#row = rows_col + 1
# If Twilio's request to our app included already gathered digits,
# process them
if 'Digits' in request.values:
# Get which digit the caller chose
choice = request.values['Digits']
# <Say> a different message depending on the caller's choice
if choice == '1':
resp.say('You have indicated your interest. We will get back to you soon.')
# Updating the cells (row, column, data)
worksheet.update_cell(row, col, "Yes")
return str(resp)
elif choice == '2':
resp.say('We understand your choice. Thank you.')
# Updating the cells (row, column, data)
worksheet.update_cell(row, col, "No")
return str(resp)
else:
# If the caller didn't choose 1 or 2, apologize and ask them again
# Updating the cells (row, column, data)
###### sheet.update_cell(row, num_col, "Nil")
worksheet.update_cell(row, col, "Never indicate")
resp.say("Sorry, I don't understand that choice. Please try again.")
# If the user didn't choose 1 or 2 (or anything), send them back to /voice
resp.redirect('/voice')
return str(resp)
# if __name__ == "__main__":
# app.run(debug=True, use_reloader=False)
app.run()

How to Connect an ios App With mysql Database Using Python Instead of PHP

I'm trying to access a mysql database inside an ios app and I would like to use python to read and write to the database when called from my app.
PHP Code I would like to turn into a python script
I am not sure how to write or read data from my application using mysql-connector.
I believe this is done with the $_POST[a]; but I am not sure how to get the same results using python.
I also have a sample of how I am trying to send data from my app to my database written in swiftui.
'''
import Foundation
import SwiftUI
struct CreateEventButton: View {
#State private var isPresentedEvent = false
#State private var eventid: Int = 0
#State private var eventName: String = ""
#State private var eventDescription: String = ""
#State private var selectedStartTime = Date()
#State private var selectedEndTime = Date()
#Binding var annotationSelected: Bool
func send(_ sender: Any) {
let request = NSMutableURLRequest(url: NSURL(string: "http://YOUR FILE PATH")! as URL)
request.httpMethod = "POST"
let postString = "a=\(self.eventid)&b=\(self.eventName)&=c\(self.eventDescription)&=d\(self.selectedStartTime)&=e\(self.selectedEndTime)"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(String(describing: error))")
return
}
print("response = \(String(describing: response))")
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("responseString = \(String(describing: responseString))")
}
task.resume()
self.eventName = ""
self.eventDescription = ""
self.selectedStartTime = Date()
self.selectedEndTime = Date()
}
var body: some View {
Button(action: {
self.isPresentedEvent.toggle() //trigger modal
}, label: {
Text("Create Event")})
.foregroundColor(.secondary)
.background(Color(.secondarySystemBackground))
.cornerRadius(50.0)
.sheet(isPresented: $isPresentedEvent, content:{
VStack{
TextField("Event Name", text: self.$eventName).padding()
TextField("Event Description", text: self.$eventDescription).padding()
Form {
DatePicker("When your event starts: ", selection: self.$selectedStartTime, in: Date()...)
}
Form {
DatePicker("When your event ends: ", selection: self.$selectedEndTime, in: Date()...)
}
HStack{
Button(action: {
self.isPresentedEvent.toggle()
self.annotationSelected = false
self.eventid += 1
print("Start: \(self.selectedStartTime)")
print("End: \(self.selectedEndTime)")
//send()
}, label: {
Text("Create Event")
})
Button(action: {
self.isPresentedEvent.toggle()
}, label: {
Text("Cancel")
})
}
Text("Create Event Button (Non Functional)").padding()
}
} )
}
}
'''
I am not sure what parameters to put in the sender function created here.
Any insight would be greatly appreciated.
In order to receive POST requests from the SwiiftUI app, you need to run a python webserver like flask.
1. Create a python module (file) something like this:
app.py
from flask import Flask
from flask import make_response
import mysql.connector
from flask import request
server_name = "localhost"
username = "flask"
password = "flask"
dbname = "flask"
# Create and check connection
try:
conn = mysql.connector.connect(
host=server_name,
user=username,
passwd=password,
database=dbname
)
print("Connection OK")
except e:
print("Connection failed: ", e)
mycursor = conn.cursor()
app = Flask(__name__)
#app.route('/', methods=['POST'])
def register():
name = request.form['a']
age = request.form['b']
sql = "INSERT INTO users (name, age) VALUES (%s, %s)"
val = (name, age)
try:
mycursor.execute(sql, val)
conn.commit()
conn.close()
except e:
print("Error: ", e)
return make_response("Success!", 200)
2. Make sure you have the necessary libraries installed:
pip install flask
pip install mysql-connector-python
You need to make sure your MySQL database is up and that you fill in the real credentials for the database in the code. (Default MySQL port = 3306)
3. Start the flask development webserver
export FLASK_APP=app.py # OR set FLASK_APP=app.py (for Windows)
python -m flask run
Your server should start at: http://localhost:5000
Replace: http://YOUR FILE PATH with http://localhost:5000/ in your swift code example and viola!
UPDATE: I tested this and it works. Here is the swiftUI code I generated in postman:
import Foundation
var semaphore = DispatchSemaphore (value: 0)
let parameters = "a=Lance&b=35"
let postData = parameters.data(using: .utf8)
var request = URLRequest(url: URL(string: "http://localhost:5000/")!,timeoutInterval: Double.infinity)
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {
print(String(describing: error))
return
}
print(String(data: data, encoding: .utf8)!)
semaphore.signal()
}
task.resume()
semaphore.wait()

onSnapshot() Firestore sending changes multiple times with Flask-Sockets

I'm trying to develop a web chat with Flask and Firestore. I set a flow to receive new messages from firestore (when something changes at the database) and send through websockets to UI. Something like that:
Python:
#sockets.route('/messages')
def chat_socket(ws):
message = None
def callback_snapshot(col_snapshot, changes, read_time):
with app.app_context():
Messages = []
for change in changes:
if change.type.name == 'ADDED':
Messages.append(change.document)
conversation = render_template(
'conversation.html',
Messages = Messages,
)
numberID = None
if len(col_snapshot) > 0:
for i in col_snapshot:
a = i
numberID = a.reference.parent.parent.id
response = json.dumps({
'conversation': conversation,
'numberID': numberID
})
ws.send(response)
while not ws.closed:
response = json.loads(ws.receive())
newNumberID = response['newNumberID'].strip()
query_snapshot = fn.GetMessages(newNumberID)
doc_watch = query_snapshot.on_snapshot(callback_snapshot)
if message is None:
continue
Javascript:
function messages(numberID) {
var scheme = window.location.protocol == "https:" ? 'wss://' : 'ws://';
var webSocketUri = scheme
+ window.location.hostname
+ (location.port ? ':'+location.port: '')
+ '/messages';
/* Get elements from the page */
var form = $('#chat-form');
var textarea = $('#chat-text');
var output = $('.messages');
var status = $('.messages');
var websocket = new WebSocket(webSocketUri);
websocket.onopen = function() {};
websocket.onclose = function() {};
websocket.onmessage = function(e) {
numberID = JSON.parse(e.data).numberID
conversation = JSON.parse(e.data).conversation
output.append(conversation);
if (numberID == null){
output.empty();
}};
websocket.onerror = function(e) {console.log(e);};
websocket.onopen = () => websocket.send(numberID);
};
The problem is: When I use col_snapshot as Messages, everything is ok besides I get the whole firestore Collection sent every time to the user when a message is sent. So it's totally not efficient. When I set callback only for changes, as described above, if I trigger the function more than one time, somehow I set multiple listeners for the same collection, so I get multiple "changes updates" in UI. How I can keep track of those listeners so I only set one listener per Collection?
As you can see from the documentation, you should only call GetMessages and on_snapshot once per document.
#sockets.route('/messages')
def chat_socket(ws):
message = None
def callback_snapshot(col_snapshot, changes, read_time):
with app.app_context():
# Rest of the function ...
ws.send(response)
response = json.loads(ws.receive())
numberID = response['newNumberID'].strip()
query_snapshot = fn.GetMessages(numberID)
doc_watch = query_snapshot.on_snapshot(callback_snapshot)
while not ws.closed:
newNumberID = response['newNumberID'].strip()
response = json.loads(ws.receive())
if newNumberID != numberID:
numberID = newNumberID
query_snapshot = fn.GetMessages(numberID)
doc_watch = query_snapshot.on_snapshot(callback_snapshot)

JSON data not reaching view pyramid

I'm trying to build a page where when the user presses a button a variable which initially is 0 increments with 1. This number is then sent asynchronously to the server by using jQuery AJAX.
What I have so far is:
In my __init__.py file:
def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind = engine)
Base.metadata.bind = engine
config = Configurator(settings = settings)
config.include('pyramid_jinja2')
config.add_static_view('static', 'static')
config.add_static_view('scripts', 'scripts')
# Removed the other views
config.add_route("declare_usage", '/user/{user_id}/{address_id}/declare')
config.add_route("declare_usage_json",'/user/{user_id}/{address_id}/declare.json')
config.scan()
My HTML + Jinja2:
#Removed code for simplicity
<div id="button_add">Add</div>
{{val}}
My JS:
$(document).ready(function(){
var room = 0;
jQuery.ajax({type:'POST',
url: '/user/1/5/declare', #I use a direct user ID and a direct address ID as I'm not sure how to send this to JS from Pyramid ... yet :).
data: JSON.stringify(room),
contentType: 'application/json; charset=utf-8'});
$('#button_add').click(function(){
room = room + 1;
});
});
My view code:
#view_config(route_name = 'declare_usage', renderer = 'declara.jinja2')
#view_config(route_name = 'declare_usage_json', renderer = 'json')
def declara_consum(request):
#Removed code for simplicity
val = request.POST.get('room') #I get a "None value in my html" if I change to request.json_body -> I get an error that there is no json to be parsed.
return { 'val' : val }
What happens is that when I open the debugger the POST request is successful with no data and on the page I get 2 options for 'val':
None -> When I use val = request.POST.get('room')
Error ValueError: No JSON object could be decoded -> When I use val = request.json_body
Also, still can't get it to work if in my JS i change url to be /user/1/5/declare.json and/or data to {'room' : room}
Can somebody please point out what I'm doing wrong?
you don't need another route declare_usage_json, just need separate two function like this
#view_config(route_name = 'declare_usage', renderer = 'declara.jinja2')
def declara_consum(request):
# this will response to your jinja2
return { 'val' : val }
#view_config(route_name = 'declare_usage', xhr=True, renderer = 'json')
def declara_consum_ajax(request):
# this will response to your asynchronously request
val = request.POST.get('room')
return { 'val' : val }
when you send a request using ajax, this will goto the second function.
$.ajax({
type: 'POST',
url: '/user/1/5/declare',
data: {'room' : room},
dataType: 'json'
}).done(function(response){
// update your data at html
});

request through ajax render to a page not working django

After making an ajax request data is being sent successfully to view. But the render function is not working. Render function should render to dashboard.html with data. I have imported all files needed to render. Is it because of an ajax request. Thanks in advance...
My ajax request from template
$("#campaign").click(function () {
alert("hey main")
alert($("#campaign").val());
var id = $("#campaign").val()
$.ajax({
type: "POST",
url: "{% url 'appdata' %}",
data: {
userid: id
}
}).done(function () {
alert("Data Sent: ");
}).fail(function () {
alert("error");
});
/* location.reload()*/
});
My urls.py
url(r'^publisher/appdata/?$', 'appdata', name='appdata'),
My views.py
def appdata(request):
print 'hi i am appdata, fetch me after select id'
print request
userid = request.POST['userid']
# print 'userid: %s'%userid
impressions = Impressions.objects.get(id=userid)
# print 'impressionid: %s'%impressions.id
clicks = Clicks.objects.get(id=userid)
revenues = Revenue.objects.get(id=userid)
ecpm = App.objects.get(id=userid)
member = Member.objects.get(id=userid)
campaigns_id = Campaign.objects.get(id=userid)
# print campaigns_id
# print type(campaigns_id)
apps = App.objects.all()
# print apps
# for i in apps:
# print i.name
# print len(camps)
# dict = {'i':impressions, 'c':clicks, 'r':revenues, 'e':ecpm, 'apps': apps, 'member':member, 'c_id':campaigns_id }
# print dict['i'].impressions
return render(request, 'dashboard.html', {'i':impressions, 'c':clicks, 'r':revenues, 'e':ecpm, 'apps': apps, 'member':member, 'c_id':campaigns_id })

Categories