Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
D
domotikad
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
domotika
domotikad
Commits
1da3cb6c
Commit
1da3cb6c
authored
Oct 18, 2016
by
Franco (nextime) Lanza
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Messenger bot is working!
parent
be595c32
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
243 additions
and
41 deletions
+243
-41
dmdb.py
domotika/db/dmdb.py
+15
-0
domotika.py
domotika/domotika.py
+22
-2
bot.py
domotika/web/bot.py
+147
-27
web.py
domotika/web/web.py
+59
-12
No files found.
domotika/db/dmdb.py
View file @
1da3cb6c
...
...
@@ -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
=
"""
...
...
domotika/domotika.py
View file @
1da3cb6c
...
...
@@ -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
)
...
...
domotika/web/bot.py
View file @
1da3cb6c
...
...
@@ -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
(
'OKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOKOK
OK'
)
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
])
domotika/web/web.py
View file @
1da3cb6c
...
...
@@ -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
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment