Messenger bot is working!

parent be595c32
......@@ -873,6 +873,21 @@ def updateUserData(username, pwd, email, dhome, mhome, tts=False,
log.debug(qstring)
return runOperation(qstring).addCallback(onRes)
def getMessengerLinkedUsers():
qstring = "SELECT messenger_PSID,username FROM users WHERE messenger_PSID IS NOT NULL"
return runQuery(qstring)
def add_messenger_psid(psid, username):
def onRes(res):
if res>0:
return defer.succeed(username+" correctly updated")
else:
return defer.fail("User not found")
q = "UPDATE users SET messenger_PSID='%s' WHERE username='%s'" %(psid, username)
log.debug(qstring)
return runOperation(qstring).addCallback(onRes)
def getAllPermissions(user):
qstring="""
......
......@@ -33,7 +33,7 @@ import subprocess
import time, copy
from web import proxy, mediaproxy
from twisted.web import microdom as xml
from singleton import clients, sequences, crontabs, statuses
from singleton import clients, sequences, crontabs, statuses, messengerlinks
from singleton import oldboards as oldb
from dmlib.utils import webutils as wu
from dmlib import constants as C
......@@ -74,6 +74,10 @@ ALLIP=C.IKAP_BROADCAST
ACTION_STATUS={}
_MESSENGER_LINKS = messengerlinks.MessengerLinkRegistry()
_MESSENGER_PSID = messengerlinks.MessengerPSIDRegistry()
log = logging.getLogger( 'Core' )
def converWday(wday):
......@@ -141,7 +145,7 @@ class domotikaService(service.Service):
self.thermoProgramLoop()
self.thermoprgloop=txcron.ScheduledCall(self.thermoProgramLoop)
self.thermoprgloop.start(CronSchedule('00 * * * *'))
self.startMessengerLinkedUsers()
self.actiontimer.start(int(self.config.get("general", "action_status_timer")))
self.timer.start(int(self.config.get("ikapserver", "timeupdates")))
if self.config.get("general", "timeserver").lower() in ['yes','y','1','true', 'on']:
......@@ -176,6 +180,15 @@ class domotikaService(service.Service):
self.config.get("geo", "openweathermap_appid"))
def startMessengerLinkedUsers(self):
def populateMessenger(res):
for r in res:
log.info(res)
_MESSENGER_PSID.add_link(r[0], r[1])
dmdb.getMessengerLinkedUsers().addCallback(populateMessenger)
def startVLC(self):
self.vlc = mediaproxy.VLCFactory()
self.vlc.start()
......@@ -2620,6 +2633,13 @@ class domotikaService(service.Service):
def web_on_voiceReceived(self, txt, confidence=0.0, lang="it"):
return self.voiceRecognized(txt, confidence, lang, voicesrc='RestAPI')
def web_on_add_messenger_psid(self, psid, authcode=False):
if not _MESSENGER_PSID.linkid_exists(psid):
if authcode and _MESSENGER_LINKS.linkid_exists(authcode):
username = _MESSENGER_LINKS.get_link(authcode)['username']
_MESSENGER_PSID.add_link(psid, username)
dmdb.add_messenger_psid(psid, username)
def plugin_on_registerEvent(self, event, pname, cback):
log.debug("plugin_on_registerEvent "+str(event)+" "+str(cback))
events.registerEvent(event, pname, cback)
......
......@@ -12,6 +12,13 @@ import hashlib
import hmac
import six
from domotika.singleton import messengerlinks
_MESSENGER_LINKS = messengerlinks.MessengerLinkRegistry()
_MESSENGER_PSID = messengerlinks.MessengerPSIDRegistry()
try:
#python2
from urllib import urlencode
......@@ -25,7 +32,6 @@ log = logging.getLogger( 'Webgui' )
BOTResource = RESTResource
class BotCore(object):
path = ""
......@@ -41,8 +47,6 @@ class BaseBot(BotCore):
return 'Domotika BOTs API interface V1.0'
def messengerValidator():
def checkRequest(f):
def new_f(self, request, *a, **kw):
......@@ -63,15 +67,19 @@ def messengerValidator():
return new_f
return checkRequest
class MessengerBot(BotCore):
class MessengerCore(object):
def _cfgGet(self, keyword):
return self.core.configGet('messenger', keyword)
class MessengerBot(BotCore, MessengerCore):
path = "messenger"
graphuri = 'https://graph.facebook.com/v2.6'
#graphuri = 'http://192.168.4.2/v2.6'
def __init__(self, *a, **kw):
super(MessengerBot, self).__init__(*a, **kw)
@property
def auth_args(self):
if not hasattr(self, '_auth_args'):
......@@ -84,10 +92,6 @@ class MessengerBot(BotCore):
self._auth_args = auth
return self._auth_args
def _cfgGet(self, keyword):
return self.core.configGet('messenger', keyword)
def _generateAppSecProof(self):
if six.PY2:
hmac_object = hmac.new(str(self._cfgGet('app_secret')), unicode(self._cfgGet('page_token')), hashlib.sha256)
......@@ -96,13 +100,13 @@ class MessengerBot(BotCore):
return hmac_object.hexdigest()
def _dataSent(self, res):
log.info('OKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOK')
log.info(res)
log.debug('Messenger BOT Datasent OK')
log.debug(res)
def _dataError(self, res):
log.info('=======================================================================')
log.info(res)
log.info(res.value.reasons[0].printTraceback())
log.info('Messenger BOT Datasent ERROR')
log.error(res)
log.debug(res.value.reasons[0].printTraceback())
......@@ -128,8 +132,8 @@ class MessengerBot(BotCore):
log.debug("New messenger bot request: "+str(kw))
if 'object' in kw.keys() and kw['object'] == 'page' and 'entry' in kw.keys():
for entry in kw['entry']:
#try:
if True:
try:
#if True:
pageID=entry['id']
timestamp=entry['time']
messaging=entry['messaging']
......@@ -149,31 +153,84 @@ class MessengerBot(BotCore):
self.receivedAccountLink(message)
else:
log.info("Received unknow messaging webhook for messenger BOT: "+str(kw))
#except:
# pass
except:
pass
return Response(200, 'OK')
@route("/loginfailed")
def loginFailed(self, request, *a, **kw):
k = kw.keys()
if 'account_linking_token' in k and 'redirect_uri' in k:
log.info("Passed linking uri "+kw['redirect_uri'])
luri=kw['redirect_uri']
log.info("Authorization failed: redirecting to "+luri)
return Response(302, 'Authorization failed', headers={'Location': luri})
return Response(200, "Authorization failed." )
def receivedAuthentication(self, msg):
pass
log.info("Messenger bot received authentication: "+str(msg))
def receivedMessage(self, msg):
def voiceResult(res):
log.info('VoiceResult: '+str(res))
if len(res) > 0:
if res[0] == 'Ok' and len(res) > 1:
try:
result = 'ho eseguito "'+" ".join(res[1]['clean']+'"')
except:
pass
else:
result = 'Spiacente, non ho trovato un comando corrispondente'
self.sendMessage(msg['sender']['id'], result)
log.info("Messenger bot received message: "+str(msg))
message = u'Received: '+unicode(msg['message']['text'])
txt = unicode(msg['message']['text'])
senderid = msg['sender']['id']
if not 'is_echo' in msg['message'].keys() or not msg['message']['is_echo']:
self.sendMessage(msg['sender']['id'], message)
if not _MESSENGER_PSID.linkid_exists(senderid):
log.info(_MESSENGER_PSID.links)
if txt == u'hello' or txt == u'ciao':
self.sendMessage(senderid, 'Hello, how can i help you?')
elif txt == u'?' or txt == u'help':
self.sendMessage(senderid, 'you can\'t do that.')
elif txt == u'login':
self.sendAuthRequest(senderid)
else:
self.sendMessage(senderid, txt+" come se fosse antani")
else:
if txt == u'hello' or txt == u'ciao':
self.sendMessage(senderid, 'Hello %s, how can i help you?' %(_MESSENGER_PSID.get_link(senderid)))
elif txt == u'?' or txt == u'help':
self.sendMessage(senderid, 'Ok, devo ancora implementare l\'aiuto!')
elif txt == u'logout':
self.sendMessage(senderid, 'Ok, devo ancora implementare anche il logout')
else:
self.sendMessage(senderid, 'Io ci provo a dare questo comando...')
self.core.voiceReceived(txt, confidence=1.0, lang="it").addCallback(voiceResult)
def receivedDeliveryConfirmation(self, msg):
pass
log.info("Messenger bot received delivery confirmation: "+str(msg))
def receivedPostback(self, msg):
pass
log.info("Messenger bot received postback: "+str(msg))
def receivedMessageRead(self, msg):
log.info("Messenger bot received message read: "+str(msg))
def receivedAccountLink(self, msg):
pass
log.info("Messenger bot received accountlink: "+str(msg))
senderid = msg['sender']['id']
recipient = msg['recipient']['id']
status = msg['account_linking']['status']
authcode = msg['account_linking']['authorization_code']
if status == u'linked':
self.core.add_messenger_psid(senderid, authcode)
def sendMessage(self, recipient_id, message):
payload = {
......@@ -186,6 +243,50 @@ class MessengerBot(BotCore):
}
return self.sendAPI(payload)
def sendAuthRequest(self, recipient_id):
payload = {
'recipient': {
'id': recipient_id
},
'message': {
"attachment": {
"type": "template",
"payload": {
"template_type": "generic",
"elements": [{
"title": self._cfgGet('auth_title'),
"image_url": self._cfgGet('auth_img'),
"buttons": [{
"type": "account_link",
"url": self._cfgGet('auth_host')
}]
}]
}
}
}
}
return self.sendAPI(payload)
class MessengerBotAuth(BotCore, MessengerCore):
path="messenger"
@route("/", Http.GET)
def linkUser(self, request, *a, **kw):
k = self.session.saveargs.keys()
if 'account_linking_token' in k and 'redirect_uri' in k:
luri=self.session.saveargs['redirect_uri'][0]
log.debug(str(self.session.mind.perms))
hmac_object = hmac.new(str(self.session.mind.perms.username), unicode(self._cfgGet('page_token')), hashlib.sha256)
_MESSENGER_LINKS.add_link(hmac_object.hexdigest(), self.session.mind.perms)
luri = luri+"&authorization_code="+hmac_object.hexdigest()
log.info("Authorization succeed: redirecting to "+luri)
headers={'Location': luri}
return Response(302, 'Authorization succeed', headers=headers)
return Response(200, "Authorization Succeed." )
BotApiList=(
BaseBot,
......@@ -207,6 +308,25 @@ class BotPages(rend.Page):
request = inevow.IRequest(ctx)
self.session = inevow.ISession(ctx)
request.setHeader("pragma", "no-cache")
request.postpath=['/',name]+request.postpath
#request.postpath=['/', name]+request.postpath
request.postpath=['/']+request.postpath
log.info("BOT: "+str(request.postpath))
return BOTResource([x(self.core, self.session) for x in BotApiList])
BotAuthList=(
BaseBot,
MessengerBotAuth
)
class BotAuth(rend.Page):
def childFactory(self, ctx, name):
request = inevow.IRequest(ctx)
self.session = inevow.ISession(ctx)
request.setHeader("pragma", "no-cache")
#request.postpath=['/', name]+request.postpath
request.postpath=['/']+request.postpath
log.info("BOTAUTH: "+str(request.postpath))
return BOTResource([x(self.core, self.session) for x in BotAuthList])
......@@ -51,10 +51,6 @@ import proxy, mediaproxy, rest, bot
import os, sys
from twisted.python import reflect
from twisted import cred
import twisted.cred.portal
import twisted.cred.credentials
import twisted.cred.checkers
import twisted.cred.error
from twisted.internet import defer
from nevow import appserver
import time
......@@ -112,8 +108,17 @@ class RootPage(rend.Page):
def renderHTML(self, ctx):
request = inevow.IRequest(ctx)
session = inevow.ISession(ctx)
return rend.Page.renderHTTP(self, ctx)
def child_botauth(self, ctx):
session = inevow.ISession(ctx)
log.debug("BOT AUTH callback received")
botauth = bot.BotAuth()
botauth.core = self.core
return botauth
def child_rest(self, ctx):
if str(self.core.configGet('web', 'enablerestgui')).lower() in ['yes', '1', 'y','true']:
self.rest = rest.RestPages()
......@@ -337,8 +342,50 @@ class RootPage(rend.Page):
### Authentication
from twisted.cred.error import UnauthorizedLogin
from nevow import url
class SessionWrapper(guard.SessionWrapper):
def checkLogin(self, ctx, session, segments, *a, **kw):
try:
session.saveargs = session.args
except:
pass
request = inevow.IRequest(ctx)
return guard.SessionWrapper.checkLogin(self, ctx, session, segments, *a, **kw)
def incorrectLoginError(self, error, ctx, segments, loginFailure):
""" Used as an errback upon failed login, returns a 2-tuple of a failure URL
with the query argument 'login-failure' set to the parameter
loginFailure, and an empty list of segments, to redirect to that URL.
The basis for this error URL, i.e. the part before the query string, is
taken either from the 'referer' header from the given request if one
exists, or a computed URL that points at the same page that the user is
currently looking at to attempt login. Any existing query string will
be stripped.
"""
request = inevow.IRequest(ctx)
session = inevow.ISession(ctx)
error.trap(UnauthorizedLogin)
referer = request.getHeader("referer")
if 'botauth' in segments and len(segments) is 3:
segments = ('bot', segments[1], 'loginfailed', '')
u = guard.urlToChild(ctx, *segments)
for k in session.saveargs.keys():
if k not in ['username', 'password'] and session.saveargs[k][0] not in ['Login']:
u = u.add(k, session.saveargs[k][0])
else:
if referer is not None:
u = url.URL.fromString(referer)
else:
u = guard.urlToChild(ctx, *segments)
u = u.clear()
u = u.add('login-failure', loginFailure)
return u, ()
def renderHTTP( self, ctx):
request = inevow.IRequest(ctx)
host=request.getHeader('host')
......@@ -355,13 +402,13 @@ class SessionWrapper(guard.SessionWrapper):
name = "/".join(segments)
if name=='':
name="/"
log.debug("SessionWrapper locateChild "+str(name)+" from IP:"+str(request.getClientIP()))
log.info("SessionWrapper locateChild "+str(name)+" from IP:"+str(request.getClientIP()))
if name:
if name.startswith('mediaproxy') and request.getClientIP()=='127.0.0.1':
mp = mediaproxy.MediaStreamProxy()
mp.core = self.core
return (mp, segments[1:])
if name.startswith('bot'):
if name.startswith('bot/'):
# Bypass for chat bots
chatbot = bot.BotPages()
chatbot.core = self.core
......@@ -407,7 +454,7 @@ class SessionWrapper(guard.SessionWrapper):
request.args["username"] = [request.getUser()]
request.args["password"] = [request.getPassword()]
log.debug("Calling Guard..."+str(request.args))
log.info("Calling Guard..."+str(request.args))
return guard.SessionWrapper.locateChild(self, ctx, segments)
......@@ -552,14 +599,15 @@ class LoginPage(rend.Page):
return '{"data": "SLOGGEDOUT", "uri": "'+request.path+'", "ts": '+str(time.time())+', "result": "succeed"}'
if not rme:
log.info("LOGIN FORM FOR PATH "+request.path)
return self.getStandardHTML(request.path)
log.info("LOGIN FORM FOR PATH "+request.uri)
return self.getStandardHTML(request.uri)
else:
log.info("LOGIN FROM COOKIE FOR PATH "+request.path)
log.info("LOGIN FROM COOKIE FOR PATH "+request.uri)
return rme.addCallback(self.rmelogin, request, rmec)
def getStandardHTML(self, path):
log.info(path)
html = self.html.replace("@PATH@", '/__login__'+path)
html = html.replace("@USERNAME@", '')
html = html.replace("@PASSWORD@", '')
......@@ -591,7 +639,7 @@ class LoginPage(rend.Page):
req.addCookie("Domotikad_rme", has,
path="/", secure=True, expires=expire)
html = self.html.replace("@PATH@", '/__login__'+req.path)
html = self.html.replace("@PATH@", '/__login__'+req.uri)
html = html.replace("@USERNAME@", str(user.username))
html = html.replace("@PASSWORD@", str(lp))
html = html.replace("@CHECKED@", "checked")
......@@ -609,7 +657,6 @@ class LoginPage(rend.Page):
chatbot.core = self.core
return chatbot
def childFactory(self, ctx, name):
log.debug("Login childFactory")
request = inevow.IRequest(ctx)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment