From ef7eee80724fd651c79395e42c575ecdc9fe89a2 Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Wed, 1 May 2019 13:08:41 +0200 Subject: [PATCH] Crash hugvey client on exception so it can be restarted by supervisor --- hugvey/client.py | 23 ++++++++++++++------- hugvey/story.py | 52 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/hugvey/client.py b/hugvey/client.py index 9880248..066f406 100644 --- a/hugvey/client.py +++ b/hugvey/client.py @@ -463,10 +463,10 @@ class Hugvey(object): def start(self): logger.debug('Hugvey {}, reporting'.format(self.id)) - loop = asyncio.get_event_loop() + self.loop = asyncio.get_event_loop() self.voice_server = VoiceServer( - loop=loop, + loop=self.loop, hugvey=self, config=self.config ) @@ -483,9 +483,18 @@ class Hugvey(object): logger.info('start') # self.voice_server.asyncStart(loop) # loop.run_until_complete(self.voice_server.start()) - asyncio.ensure_future(self.voice_server.start()) - asyncio.ensure_future(self.cmd_server.command_listener()) - asyncio.ensure_future(self.cmd_server.event_sender()) - asyncio.ensure_future(self.cmd_server.heartbeat()) - loop.run_forever() + asyncio.ensure_future(self.catchException(self.voice_server.start())) + asyncio.ensure_future(self.catchException(self.cmd_server.command_listener())) + asyncio.ensure_future(self.catchException(self.cmd_server.event_sender())) + asyncio.ensure_future(self.catchException(self.cmd_server.heartbeat())) + self.loop.run_forever() logger.info('done') + + async def catchException(self, awaitable): + try: + await awaitable + except Exception as e: + logger.exception(e) + logger.critical("Hugvey quiting") +# self.loop.stop() # not fully quits program for reboot + exit() diff --git a/hugvey/story.py b/hugvey/story.py index 3595f77..355ec50 100644 --- a/hugvey/story.py +++ b/hugvey/story.py @@ -485,6 +485,8 @@ class Diversion(object): self.params = params self.finaliseMethod = None self.hasHit = False + self.disabled = False + self.type = type if type == 'no_response': self.method = self._divergeIfNoResponse self.finaliseMethod = self._returnAfterNoResponse @@ -532,6 +534,7 @@ class Diversion(object): story.logger.warn(f"Invalid message selected for diversion: {self.params['notAfterMsgId']} for {self.id}") elif story.logHasMsg(msg): # story.logger.warn(f"Block diversion {self.id} because of hit message {self.params['notAfterMsgId']}") + self.disabled = True # never run it and allow following timeouts/no_responses to run return False r = await self.method(story, @@ -539,7 +542,10 @@ class Diversion(object): direction.msgTo if direction else None, direction if direction else None) if r: - self.hasHit = True + if self.type != 'repeat': + # repeat diversion should be usable infinte times + self.hasHit = True + story.addToLog(self) return r @@ -616,7 +622,7 @@ class Diversion(object): if story.currentDiversion or not msgFrom or not msgTo: return False - if story.stats['diversions']['no_response'] + 1 == self.params['timesOccured'] and story.stats['consecutiveSilentTimeouts'] >= int(self.params['consecutiveSilences']): + if story.stats['consecutiveSilentTimeouts'] >= int(self.params['consecutiveSilences']): story.stats['diversions']['no_response'] += 1 msg = story.get(self.params['msgId']) if msg is None: @@ -740,8 +746,6 @@ class Diversion(object): interval = float(self.params['interval']) if not self.params['fromLastMessage']: # (1) last spoken at all - if story.stats['diversions']['timeout_total'] + 1 != self.params['timesOccured']: - return timeSince = story.timer.getElapsed('last_speech') if story.timer.hasMark('last_speech') else story.timer.getElapsed('start') if story.timer.hasMark('last_diversion_timeout') and story.timer.getElapsed('last_diversion_timeout') > timeSince: @@ -755,8 +759,6 @@ class Diversion(object): return # if story.currentMessage.timeoutDiversionCount + 1 - if story.stats['diversions']['timeout_last'] + 1 != self.params['timesOccured']: - return if story.currentReply is not None: # still playing back @@ -974,7 +976,6 @@ class Story(object): self.logger.info(f'has variables: {self.variables}') self.logger.info(f'has {len(self.strands)} strands: {self.strands}') self.calculateFinishesForStrands() - self.logger.warn("Calculated strands!") def reset(self): self.timer.reset() @@ -1172,7 +1173,44 @@ class Story(object): Else, they are None """ diverge = False + + activeDiversions = [] + activeTimeoutDiv = None + activeTimeoutLastDiv = None + activeNoResponseDiv = None for diversion in self.diversions: + #: :type diversion: Diversion + if diversion.disabled or diversion.hasHit: + continue + + if diversion.type == 'timeout': + if diversion.params['timesOccured'] > 0: + if not diversion.params['fromLastMessage']: + # perhaps neater if we collect them in a list, and then sort by key, but this works just as well + if not activeTimeoutDiv or activeTimeoutDiv.params['timesOccured'] > diversion.params['timesOccured']: + activeTimeoutDiv = diversion + else: + if not activeTimeoutLastDiv or activeTimeoutLastDiv.params['timesOccured'] > diversion.params['timesOccured']: + activeTimeoutLastDiv = diversion + continue + + if diversion.type == 'no_response': + if diversion.params['timesOccured'] > 0: + if not activeNoResponseDiv or activeNoResponseDiv.params['timesOccured'] > diversion.params['timesOccured']: + activeNoResponseDiv = diversion + continue + + activeDiversions.append(diversion) + + if activeTimeoutDiv: + activeDiversions.append(activeTimeoutDiv) + if activeTimeoutLastDiv: + activeDiversions.append(activeTimeoutLastDiv) + if activeNoResponseDiv: + activeDiversions.append(activeNoResponseDiv) + + for diversion in activeDiversions: + # TODO: collect diversions and order by times + timesOccured (for timeout & no_response) d = await diversion.divergeIfNeeded(self, direction) if d: diverge = True