import logging import pwnagotchi.plugins as plugins from pwnagotchi.ai.epoch import Epoch # basic mood system class Automata(object): def __init__(self, config, view): self._config = config self._view = view self._epoch = Epoch(config) def _on_miss(self, who): logging.info("it looks like %s is not in range anymore :/" % who) self._epoch.track(miss=True) self._view.on_miss(who) def _on_error(self, who, e): error = "%s" % e # when we're trying to associate or deauth something that is not in range anymore # (if we are moving), we get the following error from bettercap: # error 400: 50:c7:bf:2e:d3:37 is an unknown BSSID or it is in the association skip list. if 'is an unknown BSSID' in error: self._on_miss(who) else: logging.error("%s" % e) def set_starting(self): self._view.on_starting() def set_ready(self): plugins.on('ready', self) def in_good_mood(self): return self._has_support_network_for(1.0) def _has_support_network_for(self, factor): bond_factor = self._config['personality']['bond_encounters_factor'] total_encounters = sum(peer.encounters for _, peer in self._peers.items()) support_factor = total_encounters / bond_factor return support_factor >= factor # triggered when it's a sad/bad day but you have good friends around ^_^ def set_grateful(self): self._view.on_grateful() plugins.on('grateful', self) def set_lonely(self): if not self._has_support_network_for(1.0): logging.info("unit is lonely") self._view.on_lonely() plugins.on('lonely', self) else: logging.info("unit is grateful instead of lonely") self.set_grateful() def set_bored(self): factor = self._epoch.inactive_for / self._config['personality']['bored_num_epochs'] if not self._has_support_network_for(factor): logging.warning("%d epochs with no activity -> bored" % self._epoch.inactive_for) self._view.on_bored() plugins.on('bored', self) else: logging.info("unit is grateful instead of bored") self.set_grateful() def set_sad(self): factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs'] if not self._has_support_network_for(factor): logging.warning("%d epochs with no activity -> sad" % self._epoch.inactive_for) self._view.on_sad() plugins.on('sad', self) else: logging.info("unit is grateful instead of sad") self.set_grateful() def set_excited(self): logging.warning("%d epochs with activity -> excited" % self._epoch.active_for) self._view.on_excited() plugins.on('excited', self) def set_rebooting(self): self._view.on_rebooting() plugins.on('rebooting', self) def wait_for(self, t, sleeping=True): plugins.on('sleep' if sleeping else 'wait', self, t) self._view.wait(t, sleeping) self._epoch.track(sleep=True, inc=t) def is_stale(self): return self._epoch.num_missed > self._config['personality']['max_misses_for_recon'] def any_activity(self): return self._epoch.any_activity def next_epoch(self): logging.debug("agent.next_epoch()") was_stale = self.is_stale() did_miss = self._epoch.num_missed self._epoch.next() # after X misses during an epoch, set the status to lonely if was_stale: logging.warning("agent missed %d interactions -> lonely" % did_miss) self.set_lonely() # after X times being bored, the status is set to sad elif self._epoch.inactive_for >= self._config['personality']['sad_num_epochs']: self.set_sad() # after X times being inactive, the status is set to bored elif self._epoch.inactive_for >= self._config['personality']['bored_num_epochs']: self.set_bored() # after X times being active, the status is set to happy / excited elif self._epoch.active_for >= self._config['personality']['excited_num_epochs']: self.set_excited() elif self._epoch.active_for >= 5 and self._has_support_network_for(5.0): self.set_grateful() plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data()) if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']: logging.critical("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for) self._reboot() self._epoch.blind_for = 0