-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Event system for logs and web socket communication #1523
Changes from all commits
e4a22b1
adb4864
d0f5cec
149f23b
3509a20
17e05f3
ee2b431
c8cd18e
ad8ba19
2b9ae79
8df0fc0
72cac2f
5f84496
240b4ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from logging_handler import LoggingHandler | ||
from socketio_handler import SocketIoHandler |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import logging | ||
from pokemongo_bot.event_manager import EventHandler | ||
|
||
|
||
class LoggingHandler(EventHandler): | ||
def handle_event(self, event, sender, level, data): | ||
logger = logging.getLogger(type(sender).__name__) | ||
message = '{}: {}'.format(event, str(data)) | ||
getattr(logger, level)(message) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from pokemongo_bot.event_manager import EventHandler | ||
from socketIO_client import SocketIO | ||
|
||
|
||
class SocketIoHandler(EventHandler): | ||
|
||
|
||
def __init__(self, url): | ||
super(EventHandler, self).__init__() | ||
self.host, port_str = url.split(':') | ||
self.port = int(port_str) | ||
|
||
|
||
def handle_event(self, event, sender, level, data): | ||
with SocketIO(self.host, self.port) as sio: | ||
sio.emit('bot:broadcast', {'event': event, 'data': data}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
|
||
|
||
class EventNotRegisteredException(Exception): | ||
pass | ||
|
||
|
||
class EventMalformedException(Exception): | ||
pass | ||
|
||
|
||
class EventHandler(object): | ||
def __init__(self): | ||
pass | ||
|
||
def handle_event(self, event, kwargs): | ||
raise NotImplementedError("Please implement") | ||
|
||
|
||
class EventManager(object): | ||
def __init__(self, *handlers): | ||
self._registered_events = dict() | ||
self._handlers = handlers or [] | ||
|
||
def add_handler(self, event_handler): | ||
self._handlers.append(event_handler) | ||
|
||
def register_event(self, name, parameters=None): | ||
self._registered_events[name] = parameters | ||
|
||
def emit(self, event, sender=None, level='info', data={}): | ||
if not sender: | ||
raise ArgumentError('Event needs a sender!') | ||
|
||
levels = ['info', 'warning', 'error', 'critical', 'debug'] | ||
if not level in levels: | ||
raise ArgumentError('Event level needs to be in: {}'.format(levels)) | ||
|
||
if event not in self._registered_events: | ||
raise EventNotRegisteredException("Event %s not registered..." % event) | ||
|
||
# verify params match event | ||
parameters = self._registered_events[event] | ||
for k, v in data.iteritems(): | ||
if k not in parameters: | ||
raise EventMalformedException("Event %s does not require parameter %s" % (event, k)) | ||
|
||
# send off to the handlers | ||
for handler in self._handlers: | ||
handler.handle_event(event, sender, level, data) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import socketio | ||
import logging | ||
from eventlet import wsgi | ||
from flask import Flask, render_template | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FAILED pylint inspection: E: 4, 0: Unable to import 'flask' (import-error) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔔 shaaaaaaaame 🔔 |
||
from pokemongo_bot.event_manager import EventManager | ||
from pokemongo_bot.event_handlers import LoggingHandler | ||
|
||
sio = socketio.Server(async_mode='eventlet', logging=logging.NullHandler) | ||
app = Flask(__name__) | ||
|
||
event_manager = EventManager() | ||
event_manager.add_handler(LoggingHandler()) | ||
event_manager.register_event( | ||
"websocket_client_connected", | ||
) | ||
|
||
@sio.on('bot:broadcast') | ||
def bot_broadcast(sid, env): | ||
sio.emit(env['event'], data=env['data']) | ||
|
||
@sio.on('disconnect') | ||
def disconnect(sid): | ||
print('disconnect ', sid) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import threading | ||
import eventlet | ||
import socketio | ||
import logging | ||
from eventlet import patcher, wsgi | ||
from app import app, sio | ||
|
||
patcher.monkey_patch(all=True) | ||
|
||
class SocketIoRunner(object): | ||
|
||
def __init__(self, url): | ||
self.host, port_str = url.split(':') | ||
self.port = int(port_str) | ||
self.server = None | ||
|
||
# create the thread object | ||
self.thread = threading.Thread(target=self._start_listening_blocking) | ||
|
||
# wrap Flask application with socketio's middleware | ||
self.app = socketio.Middleware(sio, app) | ||
|
||
def start_listening_async(self): | ||
wsgi.is_accepting = True | ||
self.thread.start() | ||
|
||
def stop_listening(self): | ||
wsgi.is_accepting = False | ||
|
||
def _start_listening_blocking(self): | ||
# deploy as an eventlet WSGI server | ||
listener = eventlet.listen((self.host, self.port)) | ||
self.server = wsgi.server(listener, self.app, log_output=False, debug=False) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from socketIO_client import SocketIO, LoggingNamespace, BaseNamespace | ||
|
||
|
||
def on_location(msg): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we please make this a unit test? It should be run as part of travis. Nobody is ever going to go into this file again to run it and make sure it works. An automated test will ensure that nobody ever breaks it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can do it (: |
||
print('received location: {}'.format(msg)) | ||
|
||
if __name__ == "__main__": | ||
try: | ||
socketio = SocketIO('localhost', 4000) | ||
socketio.on('location', on_location) | ||
while True: | ||
socketio.wait(seconds=5) | ||
|
||
except (KeyboardInterrupt, SystemExit): | ||
print "Exiting" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put this lower in the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TheSavior sure, I was putting here to allow easier merge of #1253
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this belong here and not in the pokecli.py file? If it was in the cli file we'd be able to send an event on startup with the bag information we are currently printing to the console, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TheSavior the event system is set up before the api setup (where bag info is being print). We can still use the event system to log it.
Nothing is printed in the CLI file.