-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4b3e4be
commit 0630dac
Showing
4 changed files
with
275 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,3 +58,5 @@ target/ | |
|
||
# Custom | ||
.ipynb_checkpoints/* | ||
src/oauth.cfg | ||
.idea/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
A chat bot based on SleekXMPP | ||
""" | ||
|
||
from __future__ import absolute_import, division, print_function | ||
|
||
import sys | ||
import logging | ||
import random | ||
from datetime import datetime, timedelta | ||
from time import sleep | ||
|
||
from sleekxmpp import ClientXMPP | ||
from sleekxmpp.exceptions import IqError, IqTimeout | ||
from src.oauth import OAuth | ||
|
||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
if sys.version_info < (3, 0): | ||
from sleekxmpp.util.misc_ops import setdefaultencoding | ||
setdefaultencoding('utf8') | ||
else: | ||
raw_input = input | ||
|
||
|
||
class ChatClient(ClientXMPP): | ||
def __init__(self, jid, oauth): | ||
ClientXMPP.__init__(self, jid, password=None) | ||
self.oauth = oauth | ||
self.msg_callback = None | ||
self.add_event_handler("session_start", self.session_started, threaded=True) | ||
self.add_event_handler("message", self.message_received) | ||
|
||
# Plugins | ||
self.register_plugin('xep_0030') # Service Discovery | ||
#self.register_plugin('xep_0199', {'keepalive': True, 'frequency': 60}) # XMPP Ping | ||
#self.register_plugin('xep_0235') | ||
#self.register_plugin('google') | ||
#self.register_plugin('google_auth') | ||
|
||
def add_msg_callback(self, func): | ||
self.msg_callback = func | ||
|
||
def get_recipient_id(self, recipient): | ||
for k in self.client_roster.keys(): | ||
if self.client_roster[k]['name'] == recipient: | ||
recipient_id = k | ||
break | ||
else: | ||
recipient_id = None | ||
return recipient_id | ||
|
||
def connect(self, *args, **kwargs): | ||
_logger.info("Connecting...") | ||
self.credentials['access_token'] = self.oauth.access_token | ||
return super(ChatClient, self).connect(*args, **kwargs) | ||
|
||
def reconnect(self, *args, **kwargs): | ||
_logger.info("Reconnecting") | ||
self.credentials['access_token'] = self.oauth.access_token | ||
return super(ChatClient, self).reconnect(*args, **kwargs) | ||
|
||
def session_started(self, event): | ||
self.send_presence() | ||
try: | ||
self.get_roster() | ||
except IqError as err: | ||
logging.error('There was an error getting the roster') | ||
logging.error(err.iq['error']['condition']) | ||
self.disconnect() | ||
except IqTimeout: | ||
logging.error('Server is taking too long to respond') | ||
self.disconnect() | ||
|
||
def send_msg(self, recipient, msg): | ||
recipient_id = self.get_recipient_id(recipient) | ||
self.send_message(mto=recipient_id, mbody=msg, mtype='chat') | ||
|
||
def message_received(self, msg): | ||
_logger.info("Got message from {}".format(msg['from'])) | ||
if self.msg_callback is None: | ||
_logger.warn("No callback for message received registered") | ||
else: | ||
self.msg_callback(msg) | ||
|
||
|
||
class State(object): | ||
not_asked = 0 | ||
asked = 1 | ||
|
||
|
||
class MedBot(object): | ||
alarm = ['Have you taken your long-acting insulin analogue?', | ||
'Hey buddy, got your insulin?', | ||
'Have you taken your daily dose of insulin?'] | ||
reminder = ['how about now?', | ||
'and now?', | ||
'... maybe now?'] | ||
praise = ['Great!', 'Good for you!', 'Well done'] | ||
give_up = ["Okay, I'am giving up!", "It can't be helped!"] | ||
|
||
def __init__(self, chat_client, recipient, max_retries=5): | ||
self.chat_client = chat_client | ||
self.chat_client.add_msg_callback(self.handle_message) | ||
self.recipient = recipient | ||
self.positive_reply = False | ||
self.curr_state = State.not_asked | ||
self.max_retries = max_retries | ||
self.retries = 0 | ||
self.retry_sleep = 1200 | ||
|
||
def send_alarm(self): | ||
_logger.info("Alarm triggered") | ||
self.positive_reply = False | ||
self.curr_state = State.asked | ||
self.retries = 0 | ||
self.chat_client.send_msg(self.recipient, | ||
random.choice(self.alarm)) | ||
while not self.positive_reply: | ||
sleep(self.retry_sleep) | ||
if not self.ask_again(): | ||
break | ||
|
||
def ask_again(self): | ||
_logger.info("Asking again?") | ||
if not self.positive_reply: | ||
if self.retries < self.max_retries: | ||
self.retries += 1 | ||
msg = random.choice(self.reminder) | ||
answer = True | ||
else: | ||
msg = random.choice(self.give_up) | ||
answer = False | ||
self.chat_client.send_msg(self.recipient, msg) | ||
return answer | ||
|
||
def handle_message(self, msg): | ||
recipient_id = self.chat_client.get_recipient_id(self.recipient) | ||
from_recipient = msg['from'].full.startswith(recipient_id) | ||
is_positive = msg['body'].lower().startswith('ja') | ||
was_asked = self.curr_state == State.asked | ||
if from_recipient and is_positive and was_asked: | ||
_logger.info("Positive reply received") | ||
self.positive_reply = True | ||
self.curr_state = State.not_asked | ||
self.chat_client.send_msg(self.recipient, | ||
random.choice(self.praise)) | ||
|
||
def _get_secs_to(self, timestamp): | ||
delta = timestamp - datetime.now() | ||
return delta.total_seconds() | ||
|
||
def _get_next_alarm(self): | ||
today = datetime.now() | ||
today_alarm = datetime(today.year, today.month, today.day, 18, 40, 0) | ||
if (today_alarm - today - timedelta(seconds=15)).days >= 0: | ||
return today_alarm | ||
else: | ||
return today_alarm + timedelta(days=1) | ||
|
||
def run(self): | ||
if self.chat_client.connect(): | ||
self.chat_client.process(block=False) | ||
while True: | ||
sleep(self._get_secs_to(self._get_next_alarm())) | ||
self.send_alarm() | ||
else: | ||
raise RuntimeError("Unable to connect!") | ||
|
||
|
||
if __name__ == '__main__': | ||
logging.basicConfig(level=logging.INFO, | ||
format='%(levelname)-8s %(message)s', | ||
stream=sys.stdout) | ||
oauth = OAuth() | ||
oauth.read_cfg('oauth.cfg') | ||
jid = '[email protected]' | ||
chat_client = ChatClient(jid, oauth) | ||
medbot = MedBot(chat_client, 'Buddy') | ||
medbot.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from __future__ import print_function, absolute_import, division | ||
|
||
import requests | ||
from ConfigParser import SafeConfigParser | ||
|
||
__author__ = 'Florian Wilhelm' | ||
__copyright__ = 'Blue Yonder' | ||
__license__ = 'new BSD' | ||
|
||
|
||
class OAuth(object): | ||
def __init__(self, client_id=None, client_secret=None, refresh_token=None): | ||
self.client_id = client_id | ||
self.client_secret = client_secret | ||
self.refresh_token = refresh_token | ||
self.url = 'https://www.googleapis.com/oauth2/v3/token' | ||
|
||
def from_cfg(self, filename): | ||
parser = SafeConfigParser() | ||
parser.readfp(filename=filename) | ||
self.client_id = parser.get('credentials', 'client_id') | ||
self.client_secret = parser.get('credentials', 'client_secret') | ||
self.refresh_token = parser.get('credentials', 'refresh_token') | ||
|
||
@property | ||
def access_token(self): | ||
params = dict(refresh_token=self.refresh_token, | ||
client_id=self.client_id, | ||
client_secret=self.client_secret, | ||
grant_type='refresh_token') | ||
resp = requests.post(self.url, data=params) | ||
resp.raise_for_status() | ||
return resp.json()["access_token"] |