From 9f9146c0f9e418c3ab07ab177983c556651b7ed6 Mon Sep 17 00:00:00 2001 From: Eevee Date: Wed, 10 Aug 2016 09:15:04 +0900 Subject: [PATCH] Add ColoredLoggingHandler (#3198) --- CONTRIBUTORS.md | 1 + configs/config.json.cluster.example | 1 + configs/config.json.example | 1 + configs/config.json.map.example | 1 + configs/config.json.path.example | 1 + configs/config.json.pokemon.example | 1 + pokecli.py | 8 + pokemongo_bot/__init__.py | 10 +- pokemongo_bot/event_handlers/__init__.py | 1 + .../event_handlers/colored_logging_handler.py | 172 ++++++++++++++++++ 10 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 pokemongo_bot/event_handlers/colored_logging_handler.py diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 57b289ab90..bb0ac15b7b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -59,3 +59,4 @@ * nikhil-pandey * thebigjc * JaapMoolenaar + * eevee-github diff --git a/configs/config.json.cluster.example b/configs/config.json.cluster.example index e4597519f6..eb507cd43c 100644 --- a/configs/config.json.cluster.example +++ b/configs/config.json.cluster.example @@ -81,6 +81,7 @@ "evolve_captured": "NONE", "catch_randomize_reticle_factor": 1.0, "catch_randomize_spin_factor": 1.0, + "logging_color": true, "catch": { "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"}, "// Example of always catching Rattata:": {}, diff --git a/configs/config.json.example b/configs/config.json.example index d2e8d4f064..08c844218c 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -89,6 +89,7 @@ "evolve_captured": "NONE", "catch_randomize_reticle_factor": 1.0, "catch_randomize_spin_factor": 1.0, + "logging_color": true, "catch": { "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"}, "// Example of always catching Rattata:": {}, diff --git a/configs/config.json.map.example b/configs/config.json.map.example index 56a006450c..cf6604976b 100644 --- a/configs/config.json.map.example +++ b/configs/config.json.map.example @@ -323,6 +323,7 @@ "evolve_captured": "NONE", "catch_randomize_reticle_factor": 1.0, "catch_randomize_spin_factor": 1.0, + "logging_color": true, "catch": { "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"}, "// Example of always catching Rattata:": {}, diff --git a/configs/config.json.path.example b/configs/config.json.path.example index 6f7b04c305..6b6619573b 100644 --- a/configs/config.json.path.example +++ b/configs/config.json.path.example @@ -83,6 +83,7 @@ "evolve_captured": "NONE", "catch_randomize_reticle_factor": 1.0, "catch_randomize_spin_factor": 1.0, + "logging_color": true, "catch": { "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"}, "// Example of always catching Rattata:": {}, diff --git a/configs/config.json.pokemon.example b/configs/config.json.pokemon.example index e7ba38dc37..1dfa01199d 100644 --- a/configs/config.json.pokemon.example +++ b/configs/config.json.pokemon.example @@ -89,6 +89,7 @@ "evolve_captured": "NONE", "catch_randomize_reticle_factor": 1.0, "catch_randomize_spin_factor": 1.0, + "logging_color": true, "catch": { "any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or" }, diff --git a/pokecli.py b/pokecli.py index 6811da7696..f59ae3acb9 100644 --- a/pokecli.py +++ b/pokecli.py @@ -395,6 +395,14 @@ def _json_loader(filename): type=float, default=5.0 ) + add_config( + parser, + load, + long_flag="--logging_color", + help="If logging_color is set to true, colorized logging handler will be used", + type=bool, + default=True + ) # Start to parse other attrs config = parser.parse_args() diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 133bb4631e..31e7eb0bd1 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -25,7 +25,7 @@ from human_behaviour import sleep from item_list import Item from metrics import Metrics -from pokemongo_bot.event_handlers import LoggingHandler, SocketIoHandler +from pokemongo_bot.event_handlers import LoggingHandler, SocketIoHandler, ColoredLoggingHandler from pokemongo_bot.socketio_server.runner import SocketIoRunner from pokemongo_bot.websocket_remote_control import WebsocketRemoteControl from pokemongo_bot.base_dir import _base_dir @@ -88,7 +88,12 @@ def start(self): random.seed() def _setup_event_system(self): - handlers = [LoggingHandler()] + handlers = [] + if self.config.logging_color: + handlers.append(ColoredLoggingHandler()) + else: + handlers.append(LoggingHandler()) + if self.config.websocket_server_url: if self.config.websocket_start_embedded_server: self.sio_runner = SocketIoRunner(self.config.websocket_server_url) @@ -103,7 +108,6 @@ def _setup_event_system(self): if self.config.websocket_remote_control: remote_control = WebsocketRemoteControl(self).start() - self.event_manager = EventManager(*handlers) self._register_events() if self.config.show_events: diff --git a/pokemongo_bot/event_handlers/__init__.py b/pokemongo_bot/event_handlers/__init__.py index f0933a0e68..a4dd6fce7f 100644 --- a/pokemongo_bot/event_handlers/__init__.py +++ b/pokemongo_bot/event_handlers/__init__.py @@ -1,2 +1,3 @@ from logging_handler import LoggingHandler from socketio_handler import SocketIoHandler +from colored_logging_handler import ColoredLoggingHandler diff --git a/pokemongo_bot/event_handlers/colored_logging_handler.py b/pokemongo_bot/event_handlers/colored_logging_handler.py new file mode 100644 index 0000000000..80e5414ae6 --- /dev/null +++ b/pokemongo_bot/event_handlers/colored_logging_handler.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +import time +import sys +import struct + +from pokemongo_bot.event_manager import EventHandler + +class ColoredLoggingHandler(EventHandler): + EVENT_COLOR_MAP = { + 'api_error': 'red', + 'bot_exit': 'red', + 'bot_start': 'green', + 'config_error': 'red', + 'egg_already_incubating': 'yellow', + 'egg_hatched': 'green', + 'future_pokemon_release': 'yellow', + 'incubate': 'green', + 'incubator_already_used': 'yellow', + 'inventory_full': 'yellow', + 'item_discard_fail': 'red', + 'item_discarded': 'green', + 'keep_best_release': 'green', + 'level_up': 'green', + 'level_up_reward': 'green', + 'location_cache_error': 'yellow', + 'location_cache_ignored': 'yellow', + 'login_failed': 'red', + 'login_successful': 'green', + 'lucky_egg_error': 'red', + 'move_to_map_pokemon_encounter': 'green', + 'move_to_map_pokemon_fail': 'red', + 'next_egg_incubates': 'yellow', + 'next_sleep': 'green', + 'no_pokeballs': 'red', + 'pokemon_appeared': 'yellow', + 'pokemon_capture_failed': 'red', + 'pokemon_caught': 'blue', + 'pokemon_evolved': 'green', + 'pokemon_fled': 'red', + 'pokemon_inventory_full': 'red', + 'pokemon_nickname_invalid': 'red', + 'pokemon_not_in_range': 'yellow', + 'pokemon_release': 'green', + 'pokemon_vanished': 'red', + 'pokestop_empty': 'yellow', + 'pokestop_searching_too_often': 'yellow', + 'rename_pokemon': 'green', + 'skip_evolve': 'yellow', + 'softban': 'red', + 'spun_pokestop': 'cyan', + 'threw_berry_failed': 'red', + 'unknown_spin_result': 'red', + 'unset_pokemon_nickname': 'red', + 'vip_pokemon': 'red', + + # event names for 'white' still here to remember that these events are already determined its color. + 'arrived_at_cluster': 'white', + 'arrived_at_fort': 'white', + 'bot_sleep': 'white', + 'catchable_pokemon': 'white', + 'found_cluster': 'white', + 'incubate_try': 'white', + 'load_cached_location': 'white', + 'location_found': 'white', + 'login_started': 'white', + 'lured_pokemon_found': 'white', + 'move_to_map_pokemon_move_towards': 'white', + 'move_to_map_pokemon_teleport_back': 'white', + 'move_to_map_pokemon_updated_map': 'white', + 'moving_to_fort': 'white', + 'moving_to_lured_fort': 'white', + 'pokemon_catch_rate': 'white', + 'pokemon_evolve_fail': 'white', + 'pokestop_on_cooldown': 'white', + 'pokestop_out_of_range': 'white', + 'polyline_request': 'white', + 'position_update': 'white', + 'set_start_location': 'white', + 'softban_fix': 'white', + 'softban_fix_done': 'white', + 'spun_fort': 'white', + 'threw_berry': 'white', + 'threw_pokeball': 'white', + 'used_lucky_egg': 'white' + } + CONTINUOUS_EVENT_NAMES = [ + 'catchable_pokemon', + 'moving_to_lured_fort', + 'spun_fort' + ] + COLOR_CODE = { + 'red': '91', + 'green': '92', + 'yellow': '93', + 'blue': '94', + 'cyan': '96' + } + + def __init__(self): + self._last_event = None + try: + # this `try ... except` is for ImportError on Windows + import fcntl + import termios + self._ioctl = fcntl.ioctl + self._TIOCGWINSZ = termios.TIOCGWINSZ + except ImportError: + self._ioctl = None + self._TIOCGWINSZ = None + + def handle_event(self, event, sender, level, formatted_msg, data): + # Prepare message string + message = None + if formatted_msg: + try: + message = formatted_msg.decode('utf-8') + except UnicodeEncodeError: + message = formatted_msg + else: + message = '{}'.format(str(data)) + + # Replace message if necessary + if event == 'catchable_pokemon': + message = 'Something rustles nearby!' + + # Truncate previous line if same event continues + if event in ColoredLoggingHandler.CONTINUOUS_EVENT_NAMES and self._last_event == event: + # Filling with "' ' * terminal_width" in order to completely clear last line + terminal_width = self._terminal_width() + if terminal_width: + sys.stdout.write('\r{}\r'.format(' ' * terminal_width)) + else: + sys.stdout.write('\r') + else: + sys.stdout.write("\n") + + color_name = None + if event in ColoredLoggingHandler.EVENT_COLOR_MAP: + color_name = ColoredLoggingHandler.EVENT_COLOR_MAP[event] + + # Change color if necessary + if event == 'egg_hatched' and data.get('pokemon', 'error') == 'error': + # `egg_hatched` event will be dispatched in both cases: hatched pokemon info is successfully taken or not. + # change color from 'green' to 'red' in case of error. + color_name = 'red' + + if color_name in ColoredLoggingHandler.COLOR_CODE: + sys.stdout.write( + '[{time}] \033[{color}m{message}\033[0m'.format( + time=time.strftime("%H:%M:%S"), + color=ColoredLoggingHandler.COLOR_CODE[color_name], + message=message + ) + ) + else: + sys.stdout.write('[{time}] {message}'.format( + time=time.strftime("%H:%M:%S"), + message=message + )) + + sys.stdout.flush() + self._last_event = event + + def _terminal_width(self): + if self._ioctl is None or self._TIOCGWINSZ is None: + return None + + h, w, hp, wp = struct.unpack('HHHH', + self._ioctl(0, self._TIOCGWINSZ, + struct.pack('HHHH', 0, 0, 0, 0))) + return w