Merge branch 'master' into display-fix

This commit is contained in:
Michael Swisher
2019-10-05 07:00:51 -07:00
committed by GitHub
73 changed files with 2358 additions and 1126 deletions

View File

@ -1,9 +1,45 @@
# main algorithm configuration
main:
# currently implemented: en (default), de, nl, it
# currently implemented: en (default), de, el, fr, it, mk, nl, se
lang: en
# custom plugins path, if null only default plugins with be loaded
plugins: null
custom_plugins:
# which plugins to load and enable
plugins:
auto-update:
enabled: false
interval: 1 # every day
auto-backup:
enabled: false
interval: 1 # every day
files:
- /root/brain.nn
- /root/brain.json
- /root/custom.yaml
- /root/handshakes
- /etc/ssh
- /etc/hostname
- /etc/hosts
- /etc/motd
- /var/log/pwnagotchi.log
commands:
- 'tar czf /tmp/backup.tar.gz {files}'
- 'scp /tmp/backup.tar.gz pwnagotchi@10.0.0.1:/home/pwnagotchi/backups/backup-$(date).tar.gz'
gps:
enabled: false
twitter:
enabled: false
consumer_key: aaa
consumer_secret: aaa
access_token_key: aaa
access_token_secret: aaa
onlinehashcrack:
enabled: false
email: ~
wpa-sec:
enabled: false
api_key: ~
# monitor interface to use
iface: mon0
# command to run to bring the mon interface up in case it's not up already
@ -15,7 +51,9 @@ main:
# if true, will not restart the wifi module
no_restart: false
# access points to ignore
whitelist: []
whitelist:
- EXAMPLE_NETWORK
- ANOTHER_EXAMPLE_NETWORK
# if not null, filter access points by this regular expression
filter: null
# cryptographic key for identity
@ -23,7 +61,7 @@ main:
ai:
# if false, only the default 'personality' will be used
enabled: false
enabled: true
path: /root/brain.nn
# 1.0 - laziness = probability of start training
laziness: 0.1
@ -95,7 +133,7 @@ ui:
# IMPORTANT: The lifespan of an eINK display depends on the cumulative amount of refreshes. If you want to
# preserve your display over time, you should set this value to 0.0 so that the display will be refreshed only
# if any of the important data fields changed (the uptime and blinking cursor won't trigger a refresh).
fps: 0.3
fps: 0.0
display:
enabled: true
rotation: 180
@ -108,13 +146,6 @@ ui:
address: '10.0.0.2'
port: 8080
# twitter bot data
twitter:
enabled: false
consumer_key: aaa
consumer_secret: aaa
access_token_key: aaa
access_token_secret: aaa
# bettercap rest api configuration
bettercap:
@ -143,6 +174,3 @@ bettercap:
- wifi.ap.new
- wifi.ap.lost
- mod.started

View File

@ -1,65 +0,0 @@
import sys
import glob
import os
import time
import subprocess
from threading import Lock
from datetime import datetime
logfile = None
loglock = Lock()
def log(msg):
tstamp = str(datetime.now())
line = "[%s] %s" % (tstamp, msg.rstrip())
print(line)
sys.stdout.flush()
if logfile is not None:
with loglock:
with open(logfile, 'a+t') as fp:
fp.write("%s\n" % line)
def secs_to_hhmmss(secs):
mins, secs = divmod(secs, 60)
hours, mins = divmod(mins, 60)
return '%02d:%02d:%02d' % (hours, mins, secs)
def total_unique_handshakes(path):
expr = os.path.join(path, "*.pcap")
return len(glob.glob(expr))
def iface_address(ifname):
output = subprocess.getoutput("/usr/sbin/ifconfig %s" % ifname)
for line in output.split("\n"):
line = line.strip()
if line.startswith("inet "):
return line.split(' ')[1].strip()
return None
def iface_channels(ifname):
channels = []
output = subprocess.getoutput("/sbin/iwlist %s freq" % ifname)
for line in output.split("\n"):
line = line.strip()
if line.startswith("Channel "):
channels.append(int(line.split()[1]))
return channels
def led(on=True):
with open('/sys/class/leds/led0/brightness', 'w+t') as fp:
fp.write("%d" % (0 if on is True else 1))
def blink(times=1, delay=0.3):
for t in range(0, times):
led(True)
time.sleep(delay)
led(False)
time.sleep(delay)
led(True)

View File

@ -1,170 +1,103 @@
#!/usr/bin/python3
import argparse
import yaml
import time
import traceback
import logging
import core
import pwnagotchi, pwnagotchi.plugins as plugins
import pwnagotchi
import pwnagotchi.utils as utils
import pwnagotchi.plugins as plugins
from pwnagotchi.log import SessionParser
from pwnagotchi.voice import Voice
from pwnagotchi.agent import Agent
from pwnagotchi.ui.display import Display
parser = argparse.ArgumentParser()
parser.add_argument('-C', '--config', action='store', dest='config', default='/root/pwnagotchi/config.yml')
parser.add_argument('-C', '--config', action='store', dest='config', default='/root/pwnagotchi/config.yml',
help='Main configuration file.')
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/root/custom.yml',
help='If this file exists, configuration will be merged and this will override default values.')
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
parser.add_argument('--clear', dest="do_clear", action="store_true", default=False,
help="Clear the ePaper display and exit.")
parser.add_argument('--debug', dest="debug", action="store_true", default=False,
help="Enable debug logs.")
args = parser.parse_args()
config = utils.load_config(args)
utils.setup_logging(args, config)
if args.do_clear:
print("clearing the display ...")
with open(args.config, 'rt') as fp:
config = yaml.safe_load(fp)
cleardisplay = config['ui']['display']['type']
if cleardisplay in ('inkyphat', 'inky'):
print("inky display")
from inky import InkyPHAT
epd = InkyPHAT(config['ui']['display']['color'])
epd.set_border(InkyPHAT.BLACK)
self._render_cb = self._inky_render
elif cleardisplay in ('papirus', 'papi'):
print("papirus display")
from pwnagotchi.ui.papirus.epd import EPD
os.environ['EPD_SIZE'] = '2.0'
epd = EPD()
epd.clear()
elif cleardisplay in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1'):
print("waveshare v1 display")
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
epd = EPD()
epd.init(epd.lut_full_update)
epd.Clear(0xFF)
elif cleardisplay in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2'):
print("waveshare v2 display")
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
epd = EPD()
epd.init(epd.FULL_UPDATE)
epd.Clear(0xff)
else:
print("unknown display type %s" % cleardisplay)
quit()
with open(args.config, 'rt') as fp:
config = yaml.safe_load(fp)
plugins.load_from_path(plugins.default_path)
if 'plugins' in config['main'] and config['main']['plugins'] is not None:
plugins.load_from_path(config['main']['plugins'])
plugins.on('loaded')
plugins.load(config)
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
agent = Agent(view=display, config=config)
core.log("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version))
# for key, value in config['personality'].items():
# core.log(" %s: %s" % (key, value))
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version))
for _, plugin in plugins.loaded.items():
core.log("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__))
logging.debug("plugin '%s' v%s loaded from %s" % (plugin.__name__, plugin.__version__, plugin.__file__))
if args.do_manual:
core.log("entering manual mode ...")
if args.do_clear:
logging.info("clearing the display ...")
display.clear()
log = SessionParser(config['main']['log'])
elif args.do_manual:
logging.info("entering manual mode ...")
core.log("the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
log.duration_human,
log.epochs,
log.train_epochs,
log.avg_reward,
log.min_reward,
log.max_reward))
log = SessionParser(config)
logging.info(
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
log.duration_human,
log.epochs,
log.train_epochs,
log.avg_reward,
log.min_reward,
log.max_reward))
while True:
display.on_manual_mode(log)
time.sleep(1)
if config['twitter']['enabled'] and log.is_new() and Agent.is_connected() and log.handshakes > 0:
import tweepy
core.log("detected a new session and internet connectivity!")
if Agent.is_connected():
plugins.on('internet_available', display, config, log)
picture = '/dev/shm/pwnagotchi.png'
else:
logging.info("entering auto mode ...")
display.update()
display.image().save(picture, 'png')
display.set('status', 'Tweeting...')
display.update()
agent.start()
try:
auth = tweepy.OAuthHandler(config['twitter']['consumer_key'], config['twitter']['consumer_secret'])
auth.set_access_token(config['twitter']['access_token_key'], config['twitter']['access_token_secret'])
api = tweepy.API(auth)
while True:
try:
# recon on all channels
agent.recon()
# get nearby access points grouped by channel
channels = agent.get_access_points_by_channel()
# check for free channels to use
agent.check_channels(channels)
# for each channel
for ch, aps in channels:
agent.set_channel(ch)
tweet = Voice(lang=config['main']['lang']).on_log_tweet(log)
api.update_with_media(filename=picture, status=tweet)
log.save_session_id()
if not agent.is_stale() and agent.any_activity():
logging.info("%d access points on channel %d" % (len(aps), ch))
core.log("tweeted: %s" % tweet)
except Exception as e:
core.log("error: %s" % e)
# for each ap on this channel
for ap in aps:
# send an association frame in order to get for a PMKID
agent.associate(ap)
# deauth all client stations in order to get a full handshake
for sta in ap['clients']:
agent.deauth(ap, sta)
quit()
core.logfile = config['main']['log']
agent.start_ai()
agent.setup_events()
agent.set_starting()
agent.start_monitor_mode()
agent.start_event_polling()
# print initial stats
agent.next_epoch()
agent.set_ready()
while True:
try:
# recon on all channels
agent.recon()
# get nearby access points grouped by channel
channels = agent.get_access_points_by_channel()
# check for free channels to use
agent.check_channels(channels)
# for each channel
for ch, aps in channels:
agent.set_channel(ch)
if not agent.is_stale() and agent.any_activity():
core.log("%d access points on channel %d" % (len(aps), ch))
# for each ap on this channel
for ap in aps:
# send an association frame in order to get for a PMKID
agent.associate(ap)
# deauth all client stations in order to get a full handshake
for sta in ap['clients']:
agent.deauth(ap, sta)
# An interesting effect of this:
#
# From Pwnagotchi's perspective, the more new access points
# and / or client stations nearby, the longer one epoch of
# its relative time will take ... basically, in Pwnagotchi's universe,
# WiFi electromagnetic fields affect time like gravitational fields
# affect ours ... neat ^_^
agent.next_epoch()
except Exception as e:
core.log("main loop exception: %s" % e)
core.log("%s" % traceback.format_exc())
# An interesting effect of this:
#
# From Pwnagotchi's perspective, the more new access points
# and / or client stations nearby, the longer one epoch of
# its relative time will take ... basically, in Pwnagotchi's universe,
# WiFi electromagnetic fields affect time like gravitational fields
# affect ours ... neat ^_^
agent.next_epoch()
except Exception as e:
logging.exception("main loop exception")

View File

@ -1,5 +1,7 @@
import subprocess
version = '1.0.0plz2'
_name = None

View File

@ -4,12 +4,12 @@ import os
import re
import socket
from datetime import datetime
import logging
import _thread
import core
import pwnagotchi.utils as utils
import pwnagotchi.plugins as plugins
from bettercap.client import Client
from pwnagotchi.bettercap import Client
from pwnagotchi.mesh.utils import AsyncAdvertiser
from pwnagotchi.ai.train import AsyncTrainer
@ -29,7 +29,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self._started_at = time.time()
self._filter = None if config['main']['filter'] is None else re.compile(config['main']['filter'])
self._current_channel = 0
self._supported_channels = core.iface_channels(config['main']['iface'])
self._supported_channels = utils.iface_channels(config['main']['iface'])
self._view = view
self._access_points = []
self._last_pwnd = None
@ -82,7 +82,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
plugins.on('rebooting', self)
def setup_events(self):
core.log("connecting to %s ..." % self.url)
logging.info("connecting to %s ..." % self.url)
for tag in self._config['bettercap']['silence']:
try:
@ -109,34 +109,44 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
s = self.session()
for iface in s['interfaces']:
if iface['name'] == mon_iface:
core.log("found monitor interface: %s" % iface['name'])
logging.info("found monitor interface: %s" % iface['name'])
has_mon = True
break
if has_mon is False:
if mon_start_cmd is not None and mon_start_cmd != '':
core.log("starting monitor interface ...")
logging.info("starting monitor interface ...")
self.run('!%s' % mon_start_cmd)
else:
core.log("waiting for monitor interface %s ..." % mon_iface)
logging.info("waiting for monitor interface %s ..." % mon_iface)
time.sleep(1)
core.log("supported channels: %s" % self._supported_channels)
core.log("handshakes will be collected inside %s" % self._config['bettercap']['handshakes'])
logging.info("supported channels: %s" % self._supported_channels)
logging.info("handshakes will be collected inside %s" % self._config['bettercap']['handshakes'])
self._reset_wifi_settings()
wifi_running = self.is_module_running('wifi')
if wifi_running and restart:
core.log("restarting wifi module ...")
self.restart('wifi.recon')
logging.debug("restarting wifi module ...")
self.restart_module('wifi.recon')
self.run('wifi.clear')
elif not wifi_running:
core.log("starting wifi module ...")
self.start('wifi.recon')
logging.debug("starting wifi module ...")
self.start_module('wifi.recon')
self.start_advertising()
def start(self):
self.start_ai()
self.setup_events()
self.set_starting()
self.start_monitor_mode()
self.start_event_polling()
# print initial stats
self.next_epoch()
self.set_ready()
def wait_for(self, t, sleeping=True):
plugins.on('sleep' if sleeping else 'wait', self, t)
self._view.wait(t, sleeping)
@ -150,13 +160,13 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
for ch in self._epoch.non_overlapping_channels:
if ch not in busy_channels:
self._epoch.non_overlapping_channels[ch] += 1
core.log("channel %d is free from %d epochs" % (ch, self._epoch.non_overlapping_channels[ch]))
logging.info("channel %d is free from %d epochs" % (ch, self._epoch.non_overlapping_channels[ch]))
elif self._epoch.non_overlapping_channels[ch] > 0:
self._epoch.non_overlapping_channels[ch] -= 1
# report any channel that has been free for at least 3 epochs
for ch, num_epochs_free in self._epoch.non_overlapping_channels.items():
if num_epochs_free >= 3:
core.log("channel %d has been free for %d epochs" % (ch, num_epochs_free))
logging.info("channel %d has been free for %d epochs" % (ch, num_epochs_free))
self.set_free_channel(ch)
def recon(self):
@ -172,14 +182,14 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if not channels:
self._current_channel = 0
# core.log("RECON %ds" % recon_time)
logging.debug("RECON %ds" % recon_time)
self.run('wifi.recon.channel clear')
else:
# core.log("RECON %ds ON CHANNELS %s" % (recon_time, ','.join(map(str, channels))))
logging.debug("RECON %ds ON CHANNELS %s" % (recon_time, ','.join(map(str, channels))))
try:
self.run('wifi.recon.channel %s' % ','.join(map(str, channels)))
except Exception as e:
core.log("error: %s" % e)
logging.exception("error")
self.wait_for(recon_time, sleeping=False)
@ -204,7 +214,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if self._filter_included(ap):
aps.append(ap)
except Exception as e:
core.log("error: %s" % e)
logging.exception("error")
aps.sort(key=lambda ap: ap['channel'])
return self.set_access_points(aps)
@ -241,7 +251,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def _update_uptime(self, s):
secs = time.time() - self._started_at
self._view.set('uptime', core.secs_to_hhmmss(secs))
self._view.set('uptime', utils.secs_to_hhmmss(secs))
self._view.set('epoch', '%04d' % self._epoch.epoch)
def _update_counters(self):
@ -261,7 +271,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if new_shakes > 0:
self._epoch.track(handshake=True, inc=new_shakes)
tot = core.total_unique_handshakes(self._config['bettercap']['handshakes'])
tot = utils.total_unique_handshakes(self._config['bettercap']['handshakes'])
txt = '%d (%d)' % (len(self._handshakes), tot)
if self._last_pwnd is not None:
@ -274,7 +284,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def _update_advertisement(self, s):
run_handshakes = len(self._handshakes)
tot_handshakes = core.total_unique_handshakes(self._config['bettercap']['handshakes'])
tot_handshakes = utils.total_unique_handshakes(self._config['bettercap']['handshakes'])
started = s['started_at'].split('.')[0]
started = datetime.strptime(started, '%Y-%m-%dT%H:%M:%S')
started = time.mktime(started.timetuple())
@ -289,7 +299,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self._view.set_closest_peer(peer)
def _save_recovery_data(self):
core.log("writing recovery data to %s ..." % RECOVERY_DATA_FILE)
logging.warning("writing recovery data to %s ..." % RECOVERY_DATA_FILE)
with open(RECOVERY_DATA_FILE, 'w') as fp:
data = {
'started_at': self._started_at,
@ -304,7 +314,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
try:
with open(RECOVERY_DATA_FILE, 'rt') as fp:
data = json.load(fp)
core.log("found recovery data: %s" % data)
logging.info("found recovery data: %s" % data)
self._started_at = data['started_at']
self._epoch.epoch = data['epoch']
self._handshakes = data['handshakes']
@ -312,7 +322,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self._last_pwnd = data['last_pwnd']
if delete:
core.log("deleting %s" % RECOVERY_DATA_FILE)
logging.info("deleting %s" % RECOVERY_DATA_FILE)
os.unlink(RECOVERY_DATA_FILE)
except:
if not no_exceptions:
@ -323,7 +333,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
self.run('events.clear')
core.log("event polling started ...")
logging.debug("event polling started ...")
while True:
time.sleep(1)
@ -349,21 +359,21 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
new_shakes += 1
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
if ap_and_station is None:
core.log("!!! captured new handshake: %s !!!" % key)
logging.warning("!!! captured new handshake: %s !!!" % key)
self._last_pwnd = ap_mac
plugins.on('handshake', self, filename, ap_mac, sta_mac)
else:
(ap, sta) = ap_and_station
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[
'hostname'] != '<hidden>' else ap_mac
core.log("!!! captured new handshake on channel %d: %s (%s) -> %s [%s (%s)] !!!" % ( \
logging.warning("!!! captured new handshake on channel %d: %s (%s) -> %s [%s (%s)] !!!" % ( \
ap['channel'],
sta['mac'], sta['vendor'],
ap['hostname'], ap['mac'], ap['vendor']))
plugins.on('handshake', self, filename, ap, sta)
except Exception as e:
core.log("error: %s" % e)
logging.exception("error")
finally:
self._update_handshakes(new_shakes)
@ -378,10 +388,10 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
return m['running']
return False
def start(self, module):
def start_module(self, module):
self.run('%s on' % module)
def restart(self, module):
def restart_module(self, module):
self.run('%s off; %s on' % (module, module))
def _has_handshake(self, bssid):
@ -404,7 +414,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
return self._history[who] < self._config['personality']['max_interactions']
def _on_miss(self, who):
core.log("it looks like %s is not in range anymore :/" % who)
logging.info("it looks like %s is not in range anymore :/" % who)
self._epoch.track(miss=True)
self._view.on_miss(who)
@ -416,18 +426,18 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if 'is an unknown BSSID' in error:
self._on_miss(who)
else:
core.log("error: %s" % e)
logging.error("%s" % e)
def associate(self, ap, throttle=0):
if self.is_stale():
core.log("recon is stale, skipping assoc(%s)" % ap['mac'])
logging.debug("recon is stale, skipping assoc(%s)" % ap['mac'])
return
if self._config['personality']['associate'] and self._should_interact(ap['mac']):
self._view.on_assoc(ap)
try:
core.log("sending association frame to %s (%s %s) on channel %d [%d clients]..." % ( \
logging.info("sending association frame to %s (%s %s) on channel %d [%d clients]..." % ( \
ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients'])))
self.run('wifi.assoc %s' % ap['mac'])
self._epoch.track(assoc=True)
@ -441,14 +451,14 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def deauth(self, ap, sta, throttle=0):
if self.is_stale():
core.log("recon is stale, skipping deauth(%s)" % sta['mac'])
logging.debug("recon is stale, skipping deauth(%s)" % sta['mac'])
return
if self._config['personality']['deauth'] and self._should_interact(sta['mac']):
self._view.on_deauth(sta)
try:
core.log("deauthing %s (%s) from %s (%s %s) on channel %d ..." % (
logging.info("deauthing %s (%s) from %s (%s %s) on channel %d ..." % (
sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel']))
self.run('wifi.deauth %s' % sta['mac'])
self._epoch.track(deauth=True)
@ -462,7 +472,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def set_channel(self, channel, verbose=True):
if self.is_stale():
core.log("recon is stale, skipping set_channel(%d)" % channel)
logging.debug("recon is stale, skipping set_channel(%d)" % channel)
return
# if in the previous loop no client stations has been deauthenticated
@ -478,10 +488,12 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
if channel != self._current_channel:
if self._current_channel != 0 and wait > 0:
if verbose:
core.log("waiting for %ds on channel %d ..." % (wait, self._current_channel))
logging.info("waiting for %ds on channel %d ..." % (wait, self._current_channel))
else:
logging.debug("waiting for %ds on channel %d ..." % (wait, self._current_channel))
self.wait_for(wait)
if verbose and self._epoch.any_activity:
core.log("CHANNEL %d" % channel)
logging.info("CHANNEL %d" % channel)
try:
self.run('wifi.recon.channel %d' % channel)
self._current_channel = channel
@ -491,7 +503,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
plugins.on('channel_hop', self, channel)
except Exception as e:
core.log("error: %s" % e)
logging.error("error: %s" % e)
def is_stale(self):
return self._epoch.num_missed > self._config['personality']['max_misses_for_recon']
@ -502,7 +514,7 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
def _reboot(self):
self.set_rebooting()
self._save_recovery_data()
core.log("rebooting the system ...")
logging.warning("rebooting the system ...")
os.system("/usr/bin/sync")
os.system("/usr/sbin/shutdown -r now")
@ -514,24 +526,24 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
# after X misses during an epoch, set the status to lonely
if was_stale:
core.log("agent missed %d interactions -> lonely" % did_miss)
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']:
core.log("%d epochs with no activity -> sad" % self._epoch.inactive_for)
logging.warning("%d epochs with no activity -> sad" % self._epoch.inactive_for)
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']:
core.log("%d epochs with no activity -> bored" % self._epoch.inactive_for)
logging.warning("%d epochs with no activity -> bored" % self._epoch.inactive_for)
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']:
core.log("%d epochs with activity -> excited" % self._epoch.active_for)
logging.warning("%d epochs with activity -> excited" % self._epoch.active_for)
self.set_excited()
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
core.log("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for)
logging.critical("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for)
self._reboot()
self._epoch.blind_for = 0

View File

@ -7,16 +7,16 @@ import warnings
# https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning
warnings.simplefilter(action='ignore', category=FutureWarning)
import core
import logging
def load(config, agent, epoch, from_disk=True):
config = config['ai']
if not config['enabled']:
core.log("ai disabled")
logging.info("ai disabled")
return False
core.log("[ai] bootstrapping dependencies ...")
logging.info("[ai] bootstrapping dependencies ...")
from stable_baselines import A2C
from stable_baselines.common.policies import MlpLstmPolicy
@ -27,16 +27,16 @@ def load(config, agent, epoch, from_disk=True):
env = wrappers.Environment(agent, epoch)
env = DummyVecEnv([lambda: env])
core.log("[ai] bootstrapping model ...")
logging.info("[ai] bootstrapping model ...")
a2c = A2C(MlpLstmPolicy, env, **config['params'])
if from_disk and os.path.exists(config['path']):
core.log("[ai] loading %s ..." % config['path'])
logging.info("[ai] loading %s ..." % config['path'])
a2c.load(config['path'], env)
else:
core.log("[ai] model created:")
logging.info("[ai] model created:")
for key, value in config['params'].items():
core.log(" %s: %s" % (key, value))
logging.info(" %s: %s" % (key, value))
return a2c

View File

@ -1,8 +1,9 @@
import time
import threading
import logging
import core
import pwnagotchi
import pwnagotchi.utils as utils
import pwnagotchi.mesh.wifi as wifi
from pwnagotchi.ai.reward import RewardFunction
@ -87,13 +88,13 @@ class Epoch(object):
aps_per_chan[ch_idx] += 1.0
sta_per_chan[ch_idx] += len(ap['clients'])
except IndexError as e:
core.log("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels))
logging.error("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels))
for peer in peers:
try:
peers_per_chan[peer.last_channel - 1] += 1.0
except IndexError as e:
core.log(
logging.error(
"got peer data on channel %d, we can store %d channels" % (peer.last_channel, wifi.NumChannels))
# normalize
@ -172,23 +173,23 @@ class Epoch(object):
self._epoch_data['reward'] = self._reward(self.epoch + 1, self._epoch_data)
self._epoch_data_ready.set()
core.log("[epoch %d] duration=%s slept_for=%s blind=%d inactive=%d active=%d hops=%d missed=%d "
"deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% temperature=%dC reward=%s" % (
self.epoch,
core.secs_to_hhmmss(self.epoch_duration),
core.secs_to_hhmmss(self.num_slept),
self.blind_for,
self.inactive_for,
self.active_for,
self.num_hops,
self.num_missed,
self.num_deauths,
self.num_assocs,
self.num_shakes,
cpu * 100,
mem * 100,
temp,
self._epoch_data['reward']))
logging.info("[epoch %d] duration=%s slept_for=%s blind=%d inactive=%d active=%d hops=%d missed=%d "
"deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% temperature=%dC reward=%s" % (
self.epoch,
utils.secs_to_hhmmss(self.epoch_duration),
utils.secs_to_hhmmss(self.num_slept),
self.blind_for,
self.inactive_for,
self.active_for,
self.num_hops,
self.num_missed,
self.num_deauths,
self.num_assocs,
self.num_shakes,
cpu * 100,
mem * 100,
temp,
self._epoch_data['reward']))
self.epoch += 1
self.epoch_started = now

View File

@ -1,8 +1,8 @@
import logging
import gym
from gym import spaces
import numpy as np
import core
import pwnagotchi.ai.featurizer as featurizer
import pwnagotchi.ai.reward as reward
from pwnagotchi.ai.parameter import Parameter
@ -83,7 +83,7 @@ class Environment(gym.Env):
return params
def _next_epoch(self):
# core.log("[ai] waiting for epoch to finish ...")
logging.debug("[ai] waiting for epoch to finish ...")
return self._epoch.wait_for_epoch_data()
def _apply_policy(self, policy):
@ -110,7 +110,7 @@ class Environment(gym.Env):
return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {}
def reset(self):
# core.log("[ai] resetting environment ...")
# logging.info("[ai] resetting environment ...")
self._epoch_num = 0
state = self._next_epoch()
self.last['state'] = state
@ -120,7 +120,7 @@ class Environment(gym.Env):
def _render_histogram(self, hist):
for ch in range(featurizer.histogram_size):
if hist[ch]:
core.log(" CH %d: %s" % (ch + 1, hist[ch]))
logging.info(" CH %d: %s" % (ch + 1, hist[ch]))
def render(self, mode='human', close=False, force=False):
# when using a vectorialized environment, render gets called twice
@ -133,18 +133,13 @@ class Environment(gym.Env):
self._last_render = self._epoch_num
core.log("[ai] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs()))
core.log("[ai] REWARD: %f" % self.last['reward'])
logging.info("[ai] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs()))
logging.info("[ai] REWARD: %f" % self.last['reward'])
# core.log("[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items()))
logging.debug("[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items()))
core.log("[ai] observation:")
logging.info("[ai] observation:")
for name, value in self.last['state'].items():
if 'histogram' in name:
core.log(" %s" % name.replace('_histogram', ''))
logging.info(" %s" % name.replace('_histogram', ''))
self._render_histogram(value)
# core.log("[ai] outcome:")
# for name, value in self.last['state'].items():
# if 'histogram' not in name:
# core.log(" %s: %s" % (name, value))

View File

@ -4,8 +4,7 @@ import time
import random
import os
import json
import core
import logging
import pwnagotchi.plugins as plugins
import pwnagotchi.ai as ai
@ -56,7 +55,7 @@ class Stats(object):
def load(self):
with self._lock:
if os.path.exists(self.path) and os.path.getsize(self.path) > 0:
core.log("[ai] loading %s" % self.path)
logging.info("[ai] loading %s" % self.path)
with open(self.path, 'rt') as fp:
obj = json.load(fp)
@ -66,7 +65,7 @@ class Stats(object):
def save(self):
with self._lock:
core.log("[ai] saving %s" % self.path)
logging.info("[ai] saving %s" % self.path)
data = json.dumps({
'born_at': self.born_at,
@ -114,7 +113,7 @@ class AsyncTrainer(object):
_thread.start_new_thread(self._ai_worker, ())
def _save_ai(self):
core.log("[ai] saving model to %s ..." % self._nn_path)
logging.info("[ai] saving model to %s ..." % self._nn_path)
temp = "%s.tmp" % self._nn_path
self._model.save(temp)
os.replace(temp, self._nn_path)
@ -133,15 +132,15 @@ class AsyncTrainer(object):
def on_ai_policy(self, new_params):
plugins.on('ai_policy', self, new_params)
core.log("[ai] setting new policy:")
logging.info("[ai] setting new policy:")
for name, value in new_params.items():
if name in self._config['personality']:
curr_value = self._config['personality'][name]
if curr_value != value:
core.log("[ai] ! %s: %s -> %s" % (name, curr_value, value))
logging.info("[ai] ! %s: %s -> %s" % (name, curr_value, value))
self._config['personality'][name] = value
else:
core.log("[ai] param %s not in personality configuration!" % name)
logging.error("[ai] param %s not in personality configuration!" % name)
self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl'])
self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl'])
@ -152,12 +151,12 @@ class AsyncTrainer(object):
plugins.on('ai_ready', self)
def on_ai_best_reward(self, r):
core.log("[ai] best reward so far: %s" % r)
logging.info("[ai] best reward so far: %s" % r)
self._view.on_motivated(r)
plugins.on('ai_best_reward', self, r)
def on_ai_worst_reward(self, r):
core.log("[ai] worst reward so far: %s" % r)
logging.info("[ai] worst reward so far: %s" % r)
self._view.on_demotivated(r)
plugins.on('ai_worst_reward', self, r)
@ -174,12 +173,12 @@ class AsyncTrainer(object):
self._model.env.render()
# enter in training mode?
if random.random() > self._config['ai']['laziness']:
core.log("[ai] learning for %d epochs ..." % epochs_per_episode)
logging.info("[ai] learning for %d epochs ..." % epochs_per_episode)
try:
self.set_training(True, epochs_per_episode)
self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step)
except Exception as e:
core.log("[ai] error while training: %s" % e)
logging.exception("[ai] error while training")
finally:
self.set_training(False)
obs = self._model.env.reset()

View File

@ -1,8 +1,7 @@
import logging
import requests
from requests.auth import HTTPBasicAuth
import core
class Client(object):
def __init__(self, hostname='localhost', scheme='http', port=8081, username='user', password='pass'):
@ -19,11 +18,11 @@ class Client(object):
return r.json()
except Exception as e:
if r.status_code == 200:
core.log("error while decoding json: error='%s' resp='%s'" % (e, r.text))
logging.error("error while decoding json: error='%s' resp='%s'" % (e, r.text))
else:
err = "error %d: %s" % (r.status_code, r.text.strip())
if verbose_errors:
core.log(err)
logging.info(err)
raise Exception(err)
return r.text

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 12:22+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n"
"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n"
@ -16,217 +16,169 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr ""
#: voice.py:22
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Hi, ich bin ein Pwnagotchi! Starte ..."
#: voice.py:23
msgid "New day, new hunt, new pwns!"
msgstr "Neuer Tag, neue Jagd, neue Pwns!"
#: voice.py:24
msgid "Hack the Planet!"
msgstr "Hack den Planet!"
#: voice.py:28
msgid "AI ready."
msgstr "KI bereit."
#: voice.py:29
msgid "The neural network is ready."
msgstr "Das neurale Netz ist bereit."
#: voice.py:37
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hey, Channel {channel} ist frei! Dein AP wir des dir danken."
#: voice.py:41
msgid "I'm bored ..."
msgstr "Mir ist langweilig..."
#: voice.py:42
msgid "Let's go for a walk!"
msgstr "Lass uns laufen gehen!"
#: voice.py:45
msgid "This is the best day of my life!"
msgstr "Das ist der beste Tag meines Lebens."
#: voice.py:48
msgid "Shitty day :/"
msgstr "Scheis Tag :/"
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr "Mir ist sau langweilig..."
#: voice.py:53
msgid "I'm very sad ..."
msgstr "Ich bin sehr traurig..."
#: voice.py:54
msgid "I'm sad"
msgstr "Ich bin traurig"
#: voice.py:59
msgid "I'm living the life!"
msgstr "Ich lebe das Leben!"
#: voice.py:60
msgid "I pwn therefore I am."
msgstr "Ich pwne, also bin ich."
#: voice.py:61
msgid "So many networks!!!"
msgstr "So viele Netwerke!!!"
#: voice.py:62
msgid "I'm having so much fun!"
msgstr "Ich habe sooo viel Spaß!"
#: voice.py:63
msgid "My crime is that of curiosity ..."
msgstr "Mein Verbrechen ist das der Neugier ..."
#: voice.py:67
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Hallo {name}, nett Dich kennenzulernen."
#: voice.py:68
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "Gerät {name} ist in der nähe!!"
#: voice.py:72
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm ...tschüß {name}"
#: voice.py:73
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} ist weg ..."
#: voice.py:77
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoops ...{name} ist weg."
#: voice.py:78
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} verpasst!"
#: voice.py:79
msgid "Missed!"
msgstr "Verpasst!"
#: voice.py:83
msgid "Nobody wants to play with me ..."
msgstr "Niemand will mit mir spielen ..."
#: voice.py:84
msgid "I feel so alone ..."
msgstr "Ich fühl michso alleine ..."
#: voice.py:85
msgid "Where's everybody?!"
msgstr "Wo sind denn alle?"
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Schlafe für {secs}s"
#: voice.py:90
msgid "Zzzzz"
msgstr ""
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Warte für {secs}s ..."
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Schaue mich um ({secs}s)"
#: voice.py:106
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hey {what}, lass uns Freunde sein!"
#: voice.py:107
#, python-brace-format
msgid "Associating to {what}"
msgstr "Verbinde mit {what}"
#: voice.py:108
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
#: voice.py:112
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Ich denke, dass {mac} kein WiFi brauch!"
#: voice.py:113
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Deauthentifiziere {mac}"
#: voice.py:114
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Kicke {mac}!"
#: voice.py:118
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Cool, wir haben {num} neue Handshake{plural}!"
#: voice.py:121
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Ops, da ist etwas schief gelaufen ...Starte neu ..."
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "{num} Stationen gekicked\n"
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} Freunde gefunden\n"
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "{num} Handshakes aufgez.\n"
#: voice.py:128
msgid "Met 1 peer"
msgstr "1 Peer getroffen."
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr "{num} Peers getroffen"
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
@ -236,3 +188,21 @@ msgstr ""
"Ich war {duration} am Pwnen und habe {deauthed} Clients gekickt! Außerdem "
"habe ich {associated} neue Freunde getroffen und {handshakes} Handshakes "
"gefressen! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "Stunden"
msgid "minutes"
msgstr "Minuten"
msgid "seconds"
msgstr "Sekunden"
msgid "hour"
msgstr "Stunde"
msgid "minute"
msgstr "Minute"
msgid "second"
msgstr "Sekunde"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 12:22+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: 2019-10-03 08:00+0000\n"
"Last-Translator: Periklis Fregkos <fregkos@gmail.com>\n"
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
@ -17,225 +17,169 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr ""
#: voice.py:22
#, fuzzy
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Γειά, είμαι το Pwnagotchi!Εκκινούμαι ..."
msgstr "Γειά, είμαι το Pwnagotchi! Εκκινούμαι ..."
#: voice.py:23
#, fuzzy
msgid "New day, new hunt, new pwns!"
msgstr "Νέα μέρα, νέο κυνήγι,νέα pwns!"
msgstr "Νέα μέρα, νέο κυνήγι, νέα pwns!"
#: voice.py:24
msgid "Hack the Planet!"
msgstr "Hackαρε τον πλανήτη!"
#: voice.py:28
msgid "AI ready."
msgstr "ΤΝ έτοιμη."
#: voice.py:29
#, fuzzy
msgid "The neural network is ready."
msgstr "Το νευρωνικό δίκτυοείναι έτοιμο."
#: voice.py:37
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θαείναι ευγνώμων."
#: voice.py:41
msgid "I'm bored ..."
msgstr "Βαριέμαι ..."
#: voice.py:42
msgid "Let's go for a walk!"
msgstr "Ας πάμε μια βόλτα!"
#: voice.py:45
#, fuzzy
msgid "This is the best day of my life!"
msgstr "Είναι η καλύτερημέρα της ζωής μου!"
#: voice.py:48
msgid "Shitty day :/"
msgstr "Σκατένια μέρα :/"
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr "Βαριέμαι πάρα πολύ ..."
#: voice.py:53
msgid "I'm very sad ..."
msgstr "Είμαι πολύ λυπημένο ..."
#: voice.py:54
msgid "I'm sad"
msgstr "Είμαι λυπημένο"
#: voice.py:59
msgid "I'm living the life!"
msgstr "Ζω την ζωή μου!"
#: voice.py:60
msgid "I pwn therefore I am."
msgstr "Pwnάρω, άρα υπάρχω."
#: voice.py:61
msgid "So many networks!!!"
msgstr "Τόσα πολλά δίκτυα!!!"
#: voice.py:62
#, fuzzy
msgid "I'm having so much fun!"
msgstr "Περνάω τέλεια!"
#: voice.py:63
#, fuzzy
msgid "My crime is that of curiosity ..."
msgstr "Η περιέργεια είναιτο μόνο έγκλημά μου ..."
#: voice.py:67
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Γειά {name}!Χάρηκα για τη γνωριμία. {name}"
#: voice.py:68
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "Η μονάδα{name}είναι κοντά! {name}"
msgstr "Η μονάδα {name} είναι κοντά! {name}"
#: voice.py:72
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Εμμ ...αντίο{name}"
msgstr "Εμμ ...αντίο {name}"
#: voice.py:73
#, fuzzy, python-brace-format
#, python-brace-format
msgid "{name} is gone ..."
msgstr "Το {name}έφυγε ..."
msgstr "Το {name} έφυγε ..."
#: voice.py:77
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Ουπς ... Εξαφανίστηκε το{name}."
msgstr "Ουπς ... Εξαφανίστηκε το {name}."
#: voice.py:78
#, fuzzy, python-brace-format
#, python-brace-format
msgid "{name} missed!"
msgstr "Έχασα το{name}!"
msgstr "Έχασα το {name}!"
#: voice.py:79
msgid "Missed!"
msgstr "Το έχασα!"
#: voice.py:83
#, fuzzy
msgid "Nobody wants to play with me ..."
msgstr "Κανείς δε θέλει ναπαίξει μαζί μου ..."
#: voice.py:84
msgid "I feel so alone ..."
msgstr "Νιώθω μοναχός μου ..."
#: voice.py:85
msgid "Where's everybody?!"
msgstr "Μα, πού πήγαν όλοι;!"
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Ξεκουράζομαι για {secs}s ..."
#: voice.py:90
msgid "Zzzzz"
msgstr ""
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Περιμένω για {secs}s ..."
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Ψάχνω τριγύρω ({secs})"
#: voice.py:106
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Εε!{what},ας γίνουμε φίλοι!"
msgstr "Εε! {what}, ας γίνουμε φίλοι!"
#: voice.py:107
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Associating to {what}"
msgstr "Συνδέομαι με το{what}"
msgstr "Συνδέομαι με το {what}"
#: voice.py:108
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Yo {what}!"
msgstr "Που'σαι ρε τρελέ{what}!"
msgstr "Που'σαι ρε τρελέ {what}!"
#: voice.py:112
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Μόλις αποφάσισα ότι η{mac}δε χρείαζεται WiFi!"
msgstr "Μόλις αποφάσισα ότι η {mac} δε χρείαζεται WiFi!"
#: voice.py:113
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Πετάω έξω την{mac}"
msgstr "Πετάω έξω την {mac}"
#: voice.py:114
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Μπανάρω την{mac}!"
msgstr "Μπανάρω την {mac}!"
#: voice.py:118
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Τέλεια δικέ μου, πήραμε {num}νέες χειραψίες!"
msgstr "Τέλεια δικέ μου, πήραμε {num} νέες χειραψίες!"
#: voice.py:121
#, fuzzy
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Ουπς, κάτιπήγε λάθος ...Επανεκκινούμαι ..."
msgstr "Ουπς, κάτιπήγε λάθος ... Επανεκκινούμαι ..."
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Έριξα {num} σταθμούς\n"
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Έκανα {num} νέους φίλους\n"
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Πήρα {num} χειραψίες\n"
#: voice.py:128
msgid "Met 1 peer"
msgstr "Γνώρισα 1 φίλο"
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr "Γνώρισα {num} φίλους"
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
@ -245,3 +189,21 @@ msgstr ""
"Pwnαρα για {duration} και έριξα {deauthed} πελάτες! Επίσης γνώρισα "
"{associated} νέους φίλους και καταβρόχθισα {handshakes} χειραψίες! "
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgid "minutes"
msgstr ""
msgid "seconds"
msgstr ""
msgid "hour"
msgstr ""
msgid "minute"
msgstr ""
msgid "second"
msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 12:22+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: 2019-10-03 10:34+0200\n"
"Last-Translator: quantumsheep <7271496+quantumsheep@users.noreply.github."
"com>\n"
@ -18,220 +18,193 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr ""
#: voice.py:22
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr ""
msgstr "Bonjour, je suis Pwnagotchi! Démarrage ..."
#: voice.py:23
msgid "New day, new hunt, new pwns!"
msgstr ""
msgstr "Nouvelle journée, nouvelle chasse, nouveau pwns!"
#: voice.py:24
msgid "Hack the Planet!"
msgstr ""
msgstr "Hack la planète!"
#: voice.py:28
msgid "AI ready."
msgstr ""
msgstr "IA prête."
#: voice.py:29
msgid "The neural network is ready."
msgstr ""
msgstr "Le réseau neuronal est prêt."
#: voice.py:37
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
msgstr "Hey, le channel {channel} est libre! Ton AP va dis merci."
#: voice.py:41
msgid "I'm bored ..."
msgstr ""
msgstr "Je m'ennuie ..."
#: voice.py:42
msgid "Let's go for a walk!"
msgstr ""
msgstr "Allons faire un tour!"
#: voice.py:45
msgid "This is the best day of my life!"
msgstr ""
msgstr "C'est le meilleur jour de ma vie!"
#: voice.py:48
msgid "Shitty day :/"
msgstr ""
msgstr "Journée de merde :/"
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr ""
msgstr "Je m'ennuie énormément ..."
#: voice.py:53
msgid "I'm very sad ..."
msgstr ""
msgstr "Je suis très triste ..."
#: voice.py:54
msgid "I'm sad"
msgstr ""
msgstr "Je suis triste"
#: voice.py:59
msgid "I'm living the life!"
msgstr ""
msgstr "Je vis la vie!"
#: voice.py:60
msgid "I pwn therefore I am."
msgstr ""
msgstr "Je pwn donc je suis."
#: voice.py:61
msgid "So many networks!!!"
msgstr ""
msgstr "Autant de réseaux!!!"
#: voice.py:62
msgid "I'm having so much fun!"
msgstr ""
msgstr "Je m'amuse tellement!"
#: voice.py:63
msgid "My crime is that of curiosity ..."
msgstr ""
msgstr "Mon crime est celui de la curiosité ..."
#: voice.py:67
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr ""
msgstr "Bonjour {name}! Ravis de te rencontrer. {name}"
#: voice.py:68
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr ""
msgstr "L'unité {name} est proche! {name}"
#: voice.py:72
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
msgstr "Hum ... au revoir {name}"
#: voice.py:73
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
msgstr "{name} est parti ..."
#: voice.py:77
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
msgstr "Oups ... {name} est parti."
#: voice.py:78
#, python-brace-format
msgid "{name} missed!"
msgstr ""
msgstr "{name} raté!"
#: voice.py:79
msgid "Missed!"
msgstr ""
msgstr "Raté!"
#: voice.py:83
msgid "Nobody wants to play with me ..."
msgstr ""
msgstr "Personne ne veut jouer avec moi ..."
#: voice.py:84
msgid "I feel so alone ..."
msgstr ""
msgstr "Je me sens si seul ..."
#: voice.py:85
msgid "Where's everybody?!"
msgstr ""
msgstr "Où est tout le monde?!"
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr ""
msgstr "Fais la sieste pendant {secs}s ..."
#: voice.py:90
msgid "Zzzzz"
msgstr ""
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr ""
msgstr "Attends pendant {secs}s ..."
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr ""
msgstr "Regarde autour ({secs}s)"
#: voice.py:106
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr ""
msgstr "Hey {what}, soyons amis!"
#: voice.py:107
#, python-brace-format
msgid "Associating to {what}"
msgstr ""
msgstr "Association à {what}"
#: voice.py:108
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
#: voice.py:112
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr ""
msgstr "Décidé à l'instant que {mac} n'a pas besoin de WiFi!"
#: voice.py:113
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
msgstr "Désauthentification de {mac}"
#: voice.py:114
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
#: voice.py:118
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr ""
msgstr "Cool, nous avons {num} nouveaux handshake{plural}!"
#: voice.py:121
msgid "Ops, something went wrong ... Rebooting ..."
msgstr ""
msgstr "Oups, quelque chose s'est mal passé ... Redémarrage ..."
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr ""
msgstr "{num} stations kick\n"
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr ""
msgstr "Fait {num} nouveaux amis\n"
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr ""
msgstr "Récupéré {num} handshakes\n"
#: voice.py:128
msgid "Met 1 peer"
msgstr ""
msgstr "1 peer rencontré"
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr ""
msgstr "{num} peers recontrés"
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
"J'ai pwn durant {duration} et kick {deauthed} clients! J'ai aussi rencontré "
"{associated} nouveaux amis and mangé {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgid "minutes"
msgstr ""
msgid "seconds"
msgstr ""
msgid "hour"
msgstr ""
msgid "minute"
msgstr ""
msgid "second"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 13:10+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: 2019-10-02 17:20+0000\n"
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
"Language: italian\n"
@ -15,219 +15,169 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr ""
#: voice.py:22
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Ciao! Piacere Pwnagotchi! Caricamento ..."
#: voice.py:23
#, fuzzy
msgid "New day, new hunt, new pwns!"
msgstr "Nuovo giorno...nuovi handshakes!!!"
#: voice.py:24
msgid "Hack the Planet!"
msgstr ""
#: voice.py:28
msgid "AI ready."
msgstr "IA pronta."
#: voice.py:29
msgid "The neural network is ready."
msgstr "La rete neurale è pronta."
#: voice.py:37
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hey, il canale {channel} è libero! Il tuo AP ringrazia."
#: voice.py:41
msgid "I'm bored ..."
msgstr "Che noia ..."
#: voice.py:42
msgid "Let's go for a walk!"
msgstr ""
"Andiamo a fare una passeggiata!"
msgstr "Andiamo a fare una passeggiata!"
#: voice.py:45
msgid "This is the best day of my life!"
msgstr "Questo è il più bel giorno della mia vita!!!!"
#: voice.py:48
msgid "Shitty day :/"
msgstr "Giorno di merda :/"
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr "Sono estremamente annoiato ..."
#: voice.py:53
msgid "I'm very sad ..."
msgstr "Sono molto triste..."
#: voice.py:54
msgid "I'm sad"
msgstr "Sono triste"
#: voice.py:59
msgid "I'm living the life!"
msgstr "Mi sento vivo!"
#: voice.py:60
msgid "I pwn therefore I am."
msgstr "Pwn ergo sum."
#: voice.py:61
msgid "So many networks!!!"
msgstr "Qui è pieno di reti!"
#: voice.py:62
msgid "I'm having so much fun!"
msgstr "Mi sto divertendo tantissimo!"
#: voice.py:63
msgid "My crime is that of curiosity ..."
msgstr ""
#: voice.py:67
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Ciao {name}! E' un piacere. {name}"
#: voice.py:68
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "L'Unità {name} è vicina! {name}"
#: voice.py:72
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm ... addio {name}, mi mancherai..."
#: voice.py:73
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} se n'è andato ..."
#: voice.py:77
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoops ...{name} se n'è andato."
#: voice.py:78
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} è scomparso..."
#: voice.py:79
msgid "Missed!"
msgstr "Ehi! Dove sei andato!?"
#: voice.py:83
msgid "Nobody wants to play with me ..."
msgstr "Nessuno vuole giocare con me..."
#: voice.py:84
msgid "I feel so alone ..."
msgstr "Mi sento così solo..."
#: voice.py:85
msgid "Where's everybody?!"
msgstr "Dove sono tutti?!"
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Schiaccio un pisolino per {secs}s ..."
#: voice.py:90
msgid "Zzzzz"
msgstr ""
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Aspetto {secs}s ..."
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Do uno sguardo qui intorno... ({secs}s)"
#: voice.py:106
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hey {what}! Diventiamo amici!"
#: voice.py:107
#, python-brace-format
msgid "Associating to {what}"
msgstr "Collegamento con {what} in corso..."
#: voice.py:108
#, python-brace-format
msgid "Yo {what}!"
msgstr "Yo {what}!"
#: voice.py:112
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Ho appena deciso che {mac} non necessita di WiFi!"
#: voice.py:113
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
#: voice.py:114
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Sto prendendo a calci {mac}!"
#: voice.py:118
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Bene, abbiamo {num} handshake{plural} in più!"
#: voice.py:121
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Ops, qualcosa è andato storto ... Riavvio ..."
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "{num} stazioni pestate\n"
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} nuovi amici\n"
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "{num} handshakes presi\n"
#: voice.py:128
msgid "Met 1 peer"
msgstr "1 peer incontrato"
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr "{num} peers incontrati"
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
@ -237,3 +187,21 @@ msgstr ""
"Ho lavorato per {duration} e preso a calci {deauthed} clients! Ho anche "
"incontrato {associate} nuovi amici e ho mangiato {handshakes} handshakes! "
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "ore"
msgid "minutes"
msgstr "minuti"
msgid "seconds"
msgstr "secondi"
msgid "hour"
msgstr "ora"
msgid "minute"
msgstr "minuto"
msgid "second"
msgstr "secondo"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 12:44+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: 2019-09-30 23:53+0200\n"
"Last-Translator: kovach <2214005+kovachwt@users.noreply.github.com>\n"
"Language-Team: \n"
@ -17,225 +17,169 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr "ДреееММмммМммм"
#: voice.py:22
#, fuzzy
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Здраво, јас сум Pwnagotchi!Почнувам ..."
msgstr "Здраво, јас сум Pwnagotchi! Почнувам ..."
#: voice.py:23
#, fuzzy
msgid "New day, new hunt, new pwns!"
msgstr "Нов ден, нов лов,ќе си газиме!"
msgstr "Нов ден, нов лов, ќе си газиме!"
#: voice.py:24
msgid "Hack the Planet!"
msgstr "Хак д Планет!"
#: voice.py:28
msgid "AI ready."
msgstr "AI спремно."
#: voice.py:29
#, fuzzy
msgid "The neural network is ready."
msgstr "Невронската мрежае спремна."
#: voice.py:37
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Еј, каналот {channel} еслободен! APто ќе тикаже фала."
#: voice.py:41
msgid "I'm bored ..."
msgstr "Досаднооо ..."
#: voice.py:42
msgid "Let's go for a walk!"
msgstr "Ајде да шетнеме!"
#: voice.py:45
#, fuzzy
msgid "This is the best day of my life!"
msgstr "Ова ми е најдобриот ден во животот!"
#: voice.py:48
msgid "Shitty day :/"
msgstr "Срање ден :/"
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr "Ултра досадно ..."
#: voice.py:53
msgid "I'm very sad ..."
msgstr "Многу тажно ..."
#: voice.py:54
msgid "I'm sad"
msgstr "Тажно"
#: voice.py:59
msgid "I'm living the life!"
msgstr "Ммхх животче!"
#: voice.py:60
msgid "I pwn therefore I am."
msgstr "Си газам значи постојам."
#: voice.py:61
msgid "So many networks!!!"
msgstr "Мммм колку мрежи!!!"
#: voice.py:62
#, fuzzy
msgid "I'm having so much fun!"
msgstr "Јухуу забавноо ее!"
#: voice.py:63
#, fuzzy
msgid "My crime is that of curiosity ..."
msgstr "Виновен сум само заљубопитност ..."
#: voice.py:67
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Здраво{name}!Мило ми е. {name}"
msgstr "Здраво{name}! Мило ми е. {name}"
#: voice.py:68
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "Опаа{name}е во близина! {name}"
msgstr "Опаа {name} е во близина! {name}"
#: voice.py:72
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Хмм ...чао{name}"
msgstr "Хмм ...чао {name}"
#: voice.py:73
#, fuzzy, python-brace-format
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name}го снема ..."
msgstr "{name} го снема ..."
#: voice.py:77
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Уупс ...{name}го снема."
msgstr "Уупс ... {name} го снема."
#: voice.py:78
#, fuzzy, python-brace-format
#, python-brace-format
msgid "{name} missed!"
msgstr "{name}промаши!"
msgstr "{name} промаши!"
#: voice.py:79
msgid "Missed!"
msgstr "Промаши!"
#: voice.py:83
#, fuzzy
msgid "Nobody wants to play with me ..."
msgstr "Никој не сака даси игра со мене ..."
#: voice.py:84
msgid "I feel so alone ..."
msgstr "Толку сам ..."
#: voice.py:85
msgid "Where's everybody?!"
msgstr "Каде се сите?!"
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Ќе дремнам {secs}с ..."
#: voice.py:90
msgid "Zzzzz"
msgstr "Дреммм"
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr "Дремммм ({secs}с)"
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Чекам {secs}с ..."
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Шарам наоколу ({secs}с)"
#: voice.py:106
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Еј{what}ајде да се дружиме!"
msgstr "Еј {what} ајде да се дружиме!"
#: voice.py:107
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Associating to {what}"
msgstr "Се закачувам на{what}"
msgstr "Се закачувам на {what}"
#: voice.py:108
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Yo {what}!"
msgstr "Јо{what}!"
msgstr "Јо {what}!"
#: voice.py:112
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Знаеш што, на{mac}не му треба WiFi!"
msgstr "Знаеш што, на {mac} не му треба WiFi!"
#: voice.py:113
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "Го деавтентицирам{mac}"
msgstr "Го деавтентицирам {mac}"
#: voice.py:114
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Кикбан{mac}!"
msgstr "Кикбан {mac}!"
#: voice.py:118
#, fuzzy, python-brace-format
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Кул, фативме {num}нови ракувања!"
msgstr "Кул, фативме {num} нови ракувања!"
#: voice.py:121
#, fuzzy
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Упс, нешто не еко што треба ...Рестартирам ..."
msgstr "Упс, нешто не еко што треба ... Рестартирам ..."
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Избацив {num} станици\n"
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} нови другарчиња\n"
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Фатив {num} ракувања\n"
#: voice.py:128
msgid "Met 1 peer"
msgstr "Запознав 1 пријател"
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr "Запознав {num} пријатели"
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
@ -245,3 +189,21 @@ msgstr ""
"Си газам веќе {duration} и избацив {deauthed} клиенти! Запознав {associated} "
"нови другарчиња и лапнав {handshakes} ракувања! #pwnagotchi #pwnlog #pwnlife "
"#hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgid "minutes"
msgstr ""
msgid "seconds"
msgstr ""
msgid "hour"
msgstr ""
msgid "minute"
msgstr ""
msgid "second"
msgstr ""

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 12:44+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: 2019-09-29 14:00+0200\n"
"Last-Translator: Justin-P <justin-p@users.noreply.github.com>\n"
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
@ -16,218 +16,169 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr "ZzzzZZzzzzZzzz"
#: voice.py:22
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Hoi, Ik ben Pwnagotchi! Opstarten ..."
#: voice.py:23
msgid "New day, new hunt, new pwns!"
msgstr "Nieuwe dag, nieuwe jacht, nieuwe pwns!"
#: voice.py:24
msgid "Hack the Planet!"
msgstr "Hack de Wereld!"
#: voice.py:28
msgid "AI ready."
msgstr "AI is klaar."
#: voice.py:29
msgid "The neural network is ready."
msgstr "Neuronen netwerkis klaar voor gebruik."
#: voice.py:37
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Hey, kanaal {channel} is vrij! Je AP zal je bedanken."
#: voice.py:41
msgid "I'm bored ..."
msgstr "Ik verveel me ..."
#: voice.py:42
msgid "Let's go for a walk!"
msgstr "Laten we een rondje lopen!"
#: voice.py:45
msgid "This is the best day of my life!"
msgstr "Dit is de beste dag van mijn leven!"
#: voice.py:48
msgid "Shitty day :/"
msgstr "Ruk dag :/"
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr "Ik verveel me kapot ..."
#: voice.py:53
msgid "I'm very sad ..."
msgstr "Ik ben ergverdrietig ..."
#: voice.py:54
msgid "I'm sad"
msgstr "Ik ben verdrietig"
#: voice.py:59
msgid "I'm living the life!"
msgstr "Beter kan het levenniet worden!"
#: voice.py:60
msgid "I pwn therefore I am."
msgstr "Ik pwn daarom besta ik."
#: voice.py:61
msgid "So many networks!!!"
msgstr "Zo veel netwerken!!!"
#: voice.py:62
msgid "I'm having so much fun!"
msgstr "Dit is zo leuk!"
#: voice.py:63
#, fuzzy
msgid "My crime is that of curiosity ..."
msgstr "Mijn enige misdrijfis mijn nieuwsgierigheid ..."
msgstr "Mijn enige misdrijf is mijn nieuwsgierigheid ..."
#: voice.py:67
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Hallo {name}! Leuk je te ontmoeten. {name}"
#: voice.py:68
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "Unit {name} is dichtbij! {name}"
#: voice.py:72
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm ...tot ziens {name}"
#: voice.py:73
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} is weg"
#: voice.py:77
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Whoopsie ...{name} is weg"
#: voice.py:78
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} gemist!"
#: voice.py:79
msgid "Missed!"
msgstr "Gemist!"
#: voice.py:83
msgid "Nobody wants to play with me ..."
msgstr "Niemand wil metmij spelen ..."
#: voice.py:84
msgid "I feel so alone ..."
msgstr "Zo alleen ..."
#: voice.py:85
msgid "Where's everybody?!"
msgstr "Waar is iedereen?!"
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Dutje doen voor {secs}s ..."
#: voice.py:90
msgid "Zzzzz"
msgstr "Zzzzz"
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Even {secs}s wachten ..."
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Rond kijken ({secs}s)"
#: voice.py:106
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hey {what}, laten we vriendenworden!"
#: voice.py:107
#, python-brace-format
msgid "Associating to {what}"
msgstr "Verbinden met {what}"
#: voice.py:108
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
#: voice.py:112
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Ik vind dat {mac} genoeg WiFiheeft gehad!"
#: voice.py:113
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr "De-autoriseren {mac}"
#: voice.py:114
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr "Ik ga {mac} even kicken!"
#: voice.py:118
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Gaaf, we hebben {num} nieuwe handshake{plural}!"
#: voice.py:121
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Oops, iets ging fout ...Rebooting ..."
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "{num} stations gekicked\n"
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "{num} nieuwe vrienden\n"
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "{num} nieuwe handshakes\n"
#: voice.py:128
msgid "Met 1 peer"
msgstr "1 peer ontmoet"
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr "{num} peers ontmoet"
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
@ -237,3 +188,21 @@ msgstr ""
"Ik heb gepwned voor {duration} and heb {deauthed} clients gekicked! Ik heb "
"ook {associated} nieuwe vrienden gevonden en heb {handshakes} handshakes "
"gegeten! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr ""
msgid "minutes"
msgstr ""
msgid "seconds"
msgstr ""
msgid "hour"
msgstr ""
msgid "minute"
msgstr ""
msgid "second"
msgstr ""

View File

@ -0,0 +1,202 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 16:47+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Mike Eriksson <mike@swedishmike.org>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: swedish\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "ZzzzZZzzzzZzzz"
msgstr ""
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr "Hej, jag är Pwnagotchi! Startar ..."
msgid "New day, new hunt, new pwns!"
msgstr "Ny dag, ny jakt, nya pwns!"
msgid "Hack the Planet!"
msgstr "Hacka planeten!"
msgid "AI ready."
msgstr "AI klar."
msgid "The neural network is ready."
msgstr "Det neurala nätverket är klart."
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr "Du, kanal {channel} är ledig! Din AP will gilla detta."
msgid "I'm bored ..."
msgstr "Jag har det så tråkigt..."
msgid "Let's go for a walk!"
msgstr "Dags för en promenad!"
msgid "This is the best day of my life!"
msgstr "Det här är den bästa dagen i mitt liv!"
msgid "Shitty day :/"
msgstr "Idag suger :/"
msgid "I'm extremely bored ..."
msgstr "Jag är extremt uttråkad ..."
msgid "I'm very sad ..."
msgstr "Jag är jätteledsen ..."
msgid "I'm sad"
msgstr "Jag är ledsen"
msgid "I'm living the life!"
msgstr "Nu leker livet!"
msgid "I pwn therefore I am."
msgstr "Jag pwnar därför är jag."
msgid "So many networks!!!"
msgstr "Så många nätverk!!!"
msgid "I'm having so much fun!"
msgstr "Fan vad skoj jag har!"
msgid "My crime is that of curiosity ..."
msgstr "Mitt brott är att vara nyfiken ..."
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr "Hejsan {name}! Trevligt att träffas {name}"
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr "Enheten {name} är nära! {name}"
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr "Uhm ... farväl {name}"
#, python-brace-format
msgid "{name} is gone ..."
msgstr "{name} är borta ..."
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr "Hoppsan ... {name} är borta."
#, python-brace-format
msgid "{name} missed!"
msgstr "{name} missade!"
msgid "Missed!"
msgstr "Bom!"
msgid "Nobody wants to play with me ..."
msgstr "Ingen vill leka med mig ..."
msgid "I feel so alone ..."
msgstr "Jag är så ensam ..."
msgid "Where's everybody?!"
msgstr "Var är alla?!"
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr "Sover för {secs}s ..."
msgid "Zzzzz"
msgstr ""
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr "Väntar {secs}s ..."
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr "Tittar omkring mig ({secs}s)"
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr "Hejsan {what} låt oss vara vänner"
#, python-brace-format
msgid "Associating to {what}"
msgstr "Ansluter till {what}"
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr "Jag bestämde just att {mac} inte behöver WiFi!"
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr "Lysande, vi har {num} ny handskakningar{plural}!"
msgid "Ops, something went wrong ... Rebooting ..."
msgstr "Hoppsan, någpt gick fel ... Startar om ..."
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr "Sparkade {num} stationer\n"
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr "Har {num} nya vänner\n"
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr "Har {num} handskakningar\n"
msgid "Met 1 peer"
msgstr "Mötte 1 jämlike"
#, python-brace-format
msgid "Met {num} peers"
msgstr "Mötte {num} jämlikar"
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr "Jag har pwnat för {duration} och sparkat ut {deauthed} klienter, Jag "
"har också träffat {associated} nya vänner och har skakat {handshakes} händer! "
"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
msgid "hours"
msgstr "timmar"
msgid "hour"
msgstr "timme"
msgid "minutes"
msgstr "minuter"
msgid "minute"
msgstr "minut"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-10-03 13:10+0200\n"
"POT-Creation-Date: 2019-10-05 14:10+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,220 +17,190 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: voice.py:18
msgid "ZzzzZZzzzzZzzz"
msgstr ""
#: voice.py:22
msgid "Hi, I'm Pwnagotchi! Starting ..."
msgstr ""
#: voice.py:23
msgid "New day, new hunt, new pwns!"
msgstr ""
#: voice.py:24
msgid "Hack the Planet!"
msgstr ""
#: voice.py:28
msgid "AI ready."
msgstr ""
#: voice.py:29
msgid "The neural network is ready."
msgstr ""
#: voice.py:37
#, python-brace-format
msgid "Hey, channel {channel} is free! Your AP will say thanks."
msgstr ""
#: voice.py:41
msgid "I'm bored ..."
msgstr ""
#: voice.py:42
msgid "Let's go for a walk!"
msgstr ""
#: voice.py:45
msgid "This is the best day of my life!"
msgstr ""
#: voice.py:48
msgid "Shitty day :/"
msgstr ""
#: voice.py:52
msgid "I'm extremely bored ..."
msgstr ""
#: voice.py:53
msgid "I'm very sad ..."
msgstr ""
#: voice.py:54
msgid "I'm sad"
msgstr ""
#: voice.py:59
msgid "I'm living the life!"
msgstr ""
#: voice.py:60
msgid "I pwn therefore I am."
msgstr ""
#: voice.py:61
msgid "So many networks!!!"
msgstr ""
#: voice.py:62
msgid "I'm having so much fun!"
msgstr ""
#: voice.py:63
msgid "My crime is that of curiosity ..."
msgstr ""
#: voice.py:67
#, python-brace-format
msgid "Hello {name}! Nice to meet you. {name}"
msgstr ""
#: voice.py:68
#, python-brace-format
msgid "Unit {name} is nearby! {name}"
msgstr ""
#: voice.py:72
#, python-brace-format
msgid "Uhm ... goodbye {name}"
msgstr ""
#: voice.py:73
#, python-brace-format
msgid "{name} is gone ..."
msgstr ""
#: voice.py:77
#, python-brace-format
msgid "Whoops ... {name} is gone."
msgstr ""
#: voice.py:78
#, python-brace-format
msgid "{name} missed!"
msgstr ""
#: voice.py:79
msgid "Missed!"
msgstr ""
#: voice.py:83
msgid "Nobody wants to play with me ..."
msgstr ""
#: voice.py:84
msgid "I feel so alone ..."
msgstr ""
#: voice.py:85
msgid "Where's everybody?!"
msgstr ""
#: voice.py:89
#, python-brace-format
msgid "Napping for {secs}s ..."
msgstr ""
#: voice.py:90
msgid "Zzzzz"
msgstr ""
#: voice.py:91
#, python-brace-format
msgid "ZzzZzzz ({secs}s)"
msgstr ""
#: voice.py:98
#, python-brace-format
msgid "Waiting for {secs}s ..."
msgstr ""
#: voice.py:100
#, python-brace-format
msgid "Looking around ({secs}s)"
msgstr ""
#: voice.py:106
#, python-brace-format
msgid "Hey {what} let's be friends!"
msgstr ""
#: voice.py:107
#, python-brace-format
msgid "Associating to {what}"
msgstr ""
#: voice.py:108
#, python-brace-format
msgid "Yo {what}!"
msgstr ""
#: voice.py:112
#, python-brace-format
msgid "Just decided that {mac} needs no WiFi!"
msgstr ""
#: voice.py:113
#, python-brace-format
msgid "Deauthenticating {mac}"
msgstr ""
#: voice.py:114
#, python-brace-format
msgid "Kickbanning {mac}!"
msgstr ""
#: voice.py:118
#, python-brace-format
msgid "Cool, we got {num} new handshake{plural}!"
msgstr ""
#: voice.py:121
msgid "Ops, something went wrong ... Rebooting ..."
msgstr ""
#: voice.py:124
#, python-brace-format
msgid "Kicked {num} stations\n"
msgstr ""
#: voice.py:125
#, python-brace-format
msgid "Made {num} new friends\n"
msgstr ""
#: voice.py:126
#, python-brace-format
msgid "Got {num} handshakes\n"
msgstr ""
#: voice.py:128
msgid "Met 1 peer"
msgstr ""
#: voice.py:130
#, python-brace-format
msgid "Met {num} peers"
msgstr ""
#: voice.py:135
#, python-brace-format
msgid ""
"I've been pwning for {duration} and kicked {deauthed} clients! I've also met "
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
"#pwnlog #pwnlife #hacktheplanet #skynet"
msgstr ""
msgid "hours"
msgstr ""
msgid "minutes"
msgstr ""
msgid "seconds"
msgstr ""
msgid "hour"
msgstr ""
msgid "minute"
msgstr ""
msgid "second"
msgstr ""

View File

@ -1,10 +1,10 @@
import os
import hashlib
import time
import re
import os
from datetime import datetime
from pwnagotchi.voice import Voice
from pwnagotchi.mesh.peer import Peer
from file_read_backwards import FileReadBackwards
@ -37,6 +37,8 @@ class SessionParser(object):
self.last_saved_session_id = self.last_session_id
def _parse_datetime(self, dt):
dt = dt.split('.')[0]
dt = dt.split(',')[0]
dt = datetime.strptime(dt.split('.')[0], '%Y-%m-%d %H:%M:%S')
return time.mktime(dt.timetuple())
@ -123,17 +125,19 @@ class SessionParser(object):
self.duration = '%02d:%02d:%02d' % (hours, mins, secs)
self.duration_human = []
if hours > 0:
self.duration_human.append('%d hours' % hours)
self.duration_human.append('%d %s' % (hours, self.voice.hhmmss(hours, 'h')))
if mins > 0:
self.duration_human.append('%d minutes' % mins)
self.duration_human.append('%d %s' % (mins, self.voice.hhmmss(mins, 'm')))
if secs > 0:
self.duration_human.append('%d seconds' % secs)
self.duration_human.append('%d %s' % (secs, self.voice.hhmmss(secs, 's')))
self.duration_human = ', '.join(self.duration_human)
self.avg_reward /= (self.epochs if self.epochs else 1)
def __init__(self, path='/var/log/pwnagotchi.log'):
self.path = path
def __init__(self, config):
self.config = config
self.voice = Voice(lang=config['main']['lang'])
self.path = config['main']['log']
self.last_session = None
self.last_session_id = ''
self.last_saved_session_id = ''

View File

@ -2,9 +2,9 @@ import time
import json
import _thread
import threading
import logging
from scapy.all import Dot11, Dot11FCS, Dot11Elt, RadioTap, sendp, sniff
import core
import pwnagotchi.ui.faces as faces
import pwnagotchi.mesh.wifi as wifi
@ -54,7 +54,6 @@ class Advertiser(object):
self._lost_peer_cb = lost_cb
def on_face_change(self, old, new):
# core.log("face change: %s -> %s" % (old, new))
self.update({'face': new})
def start(self):
@ -84,12 +83,14 @@ class Advertiser(object):
self._stopped.set()
def _sender(self):
core.log("started advertiser thread (period:%s sid:%s) ..." % (str(self._period), self._me.session_id))
logging.info("started advertiser thread (period:%s sid:%s) ..." % (str(self._period), self._me.session_id))
while self._running:
try:
sendp(self._frame, iface=self._iface, verbose=False, count=5, inter=self._period)
sendp(self._frame, iface=self._iface, verbose=False, count=1, inter=self._period)
except OSError as ose:
logging.warning("non critical issue while sending advertising packet: %s" % ose)
except Exception as e:
core.log("error: %s" % e)
logging.exception("error")
time.sleep(self._period)
def _on_advertisement(self, src_session_id, channel, rssi, adv):
@ -97,7 +98,7 @@ class Advertiser(object):
with self._peers_lock:
if ident not in self._peers:
peer = Peer(src_session_id, channel, rssi, adv)
core.log("detected unit %s (v%s) on channel %d (%s dBm) [sid:%s pwnd_tot:%d uptime:%d]" % ( \
logging.info("detected unit %s (v%s) on channel %d (%s dBm) [sid:%s pwnd_tot:%d uptime:%d]" % ( \
peer.full_name(),
peer.version(),
channel,
@ -158,10 +159,10 @@ class Advertiser(object):
raise Exception("unknown frame id %d" % dot11elt.ID)
except Exception as e:
core.log("error decoding packet from %s: %s" % (dot11.addr3, e))
logging.exception("error decoding packet from %s" % dot11.addr3)
def _listener(self):
# core.log("started advertisements listener ...")
# logging.info("started advertisements listener ...")
expr = "type mgt subtype beacon and ether src %s" % wifi.SignatureAddress
sniff(iface=self._iface, filter=expr, prn=self._on_packet, store=0, stop_filter=lambda x: self._stopped.isSet())
@ -173,7 +174,7 @@ class Advertiser(object):
for ident, peer in self._peers.items():
inactive_for = peer.inactive_for()
if inactive_for >= Advertiser.MAX_STALE_TIME:
core.log("peer %s lost (inactive for %ds)" % (peer.full_name(), inactive_for))
logging.info("peer %s lost (inactive for %ds)" % (peer.full_name(), inactive_for))
self._lost_peer_cb(peer)
stale.append(ident)

View File

@ -1,8 +1,8 @@
import time
import logging
import pwnagotchi.mesh.wifi as wifi
import pwnagotchi.ui.faces as faces
import core
class Peer(object):
@ -18,10 +18,10 @@ class Peer(object):
def update(self, sid, channel, rssi, adv):
if self.name() != adv['name']:
core.log("peer %s changed name: %s -> %s" % (self.full_name(), self.name(), adv['name']))
logging.info("peer %s changed name: %s -> %s" % (self.full_name(), self.name(), adv['name']))
if self.session_id != sid:
core.log("peer %s changed session id: %s -> %s" % (self.full_name(), self.session_id, sid))
logging.info("peer %s changed session id: %s -> %s" % (self.full_name(), self.session_id, sid))
self.presence[channel - 1] += 1
self.adv = adv

View File

@ -1,7 +1,8 @@
import _thread
import logging
import core
import pwnagotchi, pwnagotchi.plugins as plugins
import pwnagotchi
import pwnagotchi.plugins as plugins
from pwnagotchi.mesh import get_identity
@ -33,7 +34,7 @@ class AsyncAdvertiser(object):
self._advertiser.start()
self._view.on_state_change('face', self._advertiser.on_face_change)
else:
core.log("advertising is disabled")
logging.warning("advertising is disabled")
def _on_new_unit(self, peer):
self._view.on_new_peer(peer)

View File

@ -27,17 +27,34 @@ def load_from_file(filename):
return plugin_name, instance
def load_from_path(path):
def load_from_path(path, enabled=()):
global loaded
for filename in glob.glob(os.path.join(path, "*.py")):
name, plugin = load_from_file(filename)
if name in loaded:
raise Exception("plugin %s already loaded from %s" % (name, plugin.__file__))
elif not plugin.__enabled__:
elif name not in enabled:
# print("plugin %s is not enabled" % name)
pass
else:
loaded[name] = plugin
return loaded
def load(config):
enabled = [name for name, options in config['main']['plugins'].items() if 'enabled' in options and options['enabled']]
custom_path = config['main']['custom_plugins'] if 'custom_plugins' in config['main'] else None
# load default plugins
loaded = load_from_path(default_path, enabled=enabled)
# set the options
for name, plugin in loaded.items():
plugin.__dict__['OPTIONS'] = config['main']['plugins'][name]
# load custom ones
if custom_path is not None:
loaded = load_from_path(custom_path, enabled=enabled)
# set the options
for name, plugin in loaded.items():
plugin.__dict__['OPTIONS'] = config['main']['plugins'][name]
on('loaded')

View File

@ -0,0 +1,56 @@
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '1.0.0'
__name__ = 'auto-backup'
__license__ = 'GPL3'
__description__ = 'This plugin backups files when internet is availaible.'
from pwnagotchi.utils import StatusFile
import logging
import os
import subprocess
OPTIONS = dict()
READY = False
STATUS = StatusFile('/root/.auto-backup')
def on_loaded():
global READY
if 'files' not in OPTIONS or ('files' in OPTIONS and OPTIONS['files'] is None):
logging.error("AUTO-BACKUP: No files to backup.")
return
if 'interval' not in OPTIONS or ('interval' in OPTIONS and OPTIONS['interval'] is None):
logging.error("AUTO-BACKUP: Interval is not set.")
return
if 'commands' not in OPTIONS or ('commands' in OPTIONS and OPTIONS['commands'] is None):
logging.error("AUTO-BACKUP: No commands given.")
return
READY = True
def on_internet_available(display, config, log):
global STATUS
if READY:
if STATUS.newer_then_days(OPTIONS['interval']):
return
files_to_backup = " ".join(OPTIONS['files'])
try:
display.set('status', 'Backing up ...')
display.update()
for cmd in OPTIONS['commands']:
subprocess.call(cmd.format(files=files_to_backup).split(), stdout=open(os.devnull, 'wb'))
logging.info("AUTO-BACKUP: backup done")
STATUS.update()
except OSError as os_e:
logging.info(f"AUTO-BACKUP: Error: {os_e}")
display.set('status', 'Backup done!')
display.update()

View File

@ -0,0 +1,56 @@
__author__ = 'evilsocket@gmail.com'
__version__ = '1.0.0'
__name__ = 'auto-update'
__license__ = 'GPL3'
__description__ = 'This plugin performs an "apt update && apt upgrade" when internet is availaible.'
import logging
import subprocess
from pwnagotchi.utils import StatusFile
OPTIONS = dict()
READY = False
STATUS = StatusFile('/root/.auto-update')
def on_loaded():
global READY
if 'interval' not in OPTIONS or ('interval' in OPTIONS and OPTIONS['interval'] is None):
logging.error("AUTO-UPDATE: Interval is not set.")
return
READY = True
def on_internet_available(display, config, log):
global STATUS
if READY:
if STATUS.newer_then_days(OPTIONS['interval']):
return
try:
display.set('status', 'Updating ...')
display.update()
logging.info("AUTO-UPDATE: updating packages index ...")
update = subprocess.Popen('apt update -y', shell=True, stdin=None,
stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash")
update.wait()
logging.info("AUTO-UPDATE: updating packages ...")
upgrade = subprocess.Popen('apt upgrade -y', shell=True, stdin=None,
stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash")
upgrade.wait()
logging.info("AUTO-UPDATE: complete.")
STATUS.update()
except Exception as e:
logging.exception("AUTO-UPDATE ERROR")
display.set('status', 'Updated!')
display.update()

View File

@ -3,17 +3,25 @@ __version__ = '1.0.0'
__name__ = 'hello_world'
__license__ = 'GPL3'
__description__ = 'An example plugin for pwnagotchi that implements all the available callbacks.'
__enabled__ = False # IMPORTANT: set this to True to enable your plugin.
import logging
from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
import core
# Will be set with the options in config.yml config['main']['plugins'][__name__]
OPTIONS = dict()
# called when the plugin is loaded
def on_loaded():
core.log("WARNING: plugin %s should be disabled!" % __name__)
logging.warning("WARNING: plugin %s should be disabled!" % __name__)
# called in manual mode when there's internet connectivity
def on_internet_available(ui, config, log):
pass
# called to setup the ui elements
@ -39,7 +47,7 @@ def on_display_setup(display):
# called when everything is ready and the main loop is about to start
def on_ready(agent):
core.log("unit is ready")
logging.info("unit is ready")
# you can run custom bettercap commands if you want
# agent.run('ble.recon on')
# or set a custom state
@ -76,7 +84,7 @@ def on_ai_best_reward(agent, reward):
pass
# called when the AI got the best reward so far
# called when the AI got the worst reward so far
def on_ai_worst_reward(agent, reward):
pass

View File

@ -3,9 +3,8 @@ __version__ = '1.0.0'
__name__ = 'gps'
__license__ = 'GPL3'
__description__ = 'Save GPS coordinates whenever an handshake is captured.'
__enabled__ = True # set to false if you just don't use GPS
import core
import logging
import json
import os
@ -15,14 +14,14 @@ running = False
def on_loaded():
core.log("GPS plugin loaded for %s" % device)
logging.info("gps plugin loaded for %s" % device)
def on_ready(agent):
global running
if os.path.exists(device):
core.log("enabling GPS bettercap's module for %s" % device)
logging.info("enabling gps bettercap's module for %s" % device)
try:
agent.run('gps off')
except:
@ -33,7 +32,7 @@ def on_ready(agent):
agent.run('gps on')
running = True
else:
core.log("no GPS detected")
logging.warning("no GPS detected")
def on_handshake(agent, filename, access_point, client_station):
@ -42,6 +41,6 @@ def on_handshake(agent, filename, access_point, client_station):
gps = info['gps']
gps_filename = filename.replace('.pcap', '.gps.json')
core.log("saving GPS to %s (%s)" % (gps_filename, gps))
logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
with open(gps_filename, 'w+t') as fp:
json.dump(gps, fp)

View File

@ -0,0 +1,68 @@
# tempmem shows memory infos and cpu temperature
#
# totalmem usedmem freemem cputemp
#
__author__ = 'https://github.com/xenDE'
__version__ = '1.0.0'
__name__ = 'memtemp'
__license__ = 'GPL3'
__description__ = 'A plugin that will add a memory and temperature indicator'
import struct
from pwnagotchi.ui.components import LabeledValue
from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
import time
class MEMTEMP:
# set the minimum seconds before refresh the values
refresh_wait = 30
refresh_ts_last = time.time() - refresh_wait
def __init__(self):
# only import when the module is loaded and enabled
import os
def get_temp(self):
try:
temp = os.popen('/opt/vc/bin/vcgencmd measure_temp').readlines()[0].split('=')[1].replace("\n", '').replace("'","")
return 'cpu:' + temp
except:
return 'cpu:0.0C'
# cpu:37.4C
def get_mem_info(self):
try:
# includes RAM + Swap Memory:
# total, used, free = map(int, os.popen('free -t -m').readlines()[-1].split()[1:])
# without Swap, only real memory:
total, used, free = map(int, os.popen('free -t -m').readlines()[-3].split()[1:4])
return "tm:"+str(total)+" um:"+str(used)+" fm:"+str(free)
except:
return "tm:0 um:0 fm:0"
# tm:532 um:82 fm:353
memtemp = None
def on_loaded():
global memtemp
memtemp = MEMTEMP()
def on_ui_setup(ui):
ui.add_element('memtemp', LabeledValue(color=BLACK, label='SYS', value='tm:0 um:0 fm:0 0.0C', position=(0, ui.height()-28),
label_font=fonts.Bold, text_font=fonts.Medium))
def on_ui_update(ui):
if time.time() > memtemp.refresh_ts_last + memtemp.refresh_wait:
ui.set('memtemp', "%s %s" % (memtemp.get_mem_info(), memtemp.get_temp()))
memtemp.refresh_ts_last = time.time()

View File

@ -0,0 +1,84 @@
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '1.0.0'
__name__ = 'onlinehashcrack'
__license__ = 'GPL3'
__description__ = 'This plugin automatically uploades handshakes to https://onlinehashcrack.com'
import os
import logging
import requests
READY = False
ALREADY_UPLOADED = None
OPTIONS = dict()
def on_loaded():
"""
Gets called when the plugin gets loaded
"""
global READY
global EMAIL
global ALREADY_UPLOADED
if not 'email' in OPTIONS or ('email' in OPTIONS and OPTIONS['email'] is None):
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
return
try:
with open('/root/.ohc_uploads', 'r') as f:
ALREADY_UPLOADED = f.read().splitlines()
except OSError:
logging.warning('OHC: No upload-file found.')
ALREADY_UPLOADED = []
READY = True
def _upload_to_ohc(path, timeout=30):
"""
Uploads the file to onlinehashcrack.com
"""
with open(path, 'rb') as file_to_upload:
data = {'email': OPTIONS['email']}
payload = {'file': file_to_upload}
try:
result = requests.post('https://api.onlinehashcrack.com',
data=data,
files=payload,
timeout=timeout)
if 'already been sent' in result.text:
logging.warning(f"{path} was already uploaded.")
except requests.exceptions.RequestException as e:
logging.error(f"OHC: Got an exception while uploading {path} -> {e}")
raise e
def on_internet_available(display, config, log):
"""
Called in manual mode when there's internet connectivity
"""
if READY:
handshake_dir = config['bettercap']['handshakes']
handshake_filenames = os.listdir(handshake_dir)
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames]
handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED)
if handshake_new:
logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onelinehashcrack.com")
for idx, handshake in enumerate(handshake_new):
display.set('status', f"Uploading handshake to onlinehashcrack.com ({idx + 1}/{len(handshake_new)})")
display.update(force=True)
try:
_upload_to_ohc(handshake)
ALREADY_UPLOADED.append(handshake)
with open('/root/.ohc_uploads', 'a') as f:
f.write(handshake + "\n")
logging.info(f"OHC: Successfuly uploaded {handshake}")
except requests.exceptions.RequestException:
pass
except OSError as os_e:
logging.error(f"OHC: Got the following error: {os_e}")

View File

@ -0,0 +1,46 @@
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '1.0.0'
__name__ = 'twitter'
__license__ = 'GPL3'
__description__ = 'This plugin creates tweets about the recent activity of pwnagotchi'
import logging
from pwnagotchi.voice import Voice
OPTIONS = dict()
def on_loaded():
logging.info("twitter plugin loaded.")
# called in manual mode when there's internet connectivity
def on_internet_available(ui, config, log):
if log.is_new() and log.handshakes > 0:
try:
import tweepy
except ImportError:
logging.error("Couldn't import tweepy")
return
logging.info("detected a new session and internet connectivity!")
picture = '/dev/shm/pwnagotchi.png'
ui.on_manual_mode(log)
ui.update(force=True)
ui.image().save(picture, 'png')
ui.set('status', 'Tweeting...')
ui.update(force=True)
try:
auth = tweepy.OAuthHandler(OPTIONS['consumer_key'], OPTIONS['consumer_secret'])
auth.set_access_token(OPTIONS['access_token_key'], OPTIONS['access_token_secret'])
api = tweepy.API(auth)
tweet = Voice(lang=config['main']['lang']).on_log_tweet(log)
api.update_with_media(filename=picture, status=tweet)
log.save_session_id()
logging.info("tweeted: %s" % tweet)
except Exception as e:
logging.exception("error while tweeting")

View File

@ -12,7 +12,6 @@ __version__ = '1.0.0'
__name__ = 'ups_lite'
__license__ = 'GPL3'
__description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.1'
__enabled__ = False
import struct

View File

@ -0,0 +1,83 @@
__author__ = '33197631+dadav@users.noreply.github.com'
__version__ = '1.0.0'
__name__ = 'wpa-sec'
__license__ = 'GPL3'
__description__ = 'This plugin automatically uploades handshakes to https://wpa-sec.stanev.org'
import os
import logging
import requests
READY = False
ALREADY_UPLOADED = None
def on_loaded():
"""
Gets called when the plugin gets loaded
"""
global READY
global API_KEY
global ALREADY_UPLOADED
if not 'api_key' in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None):
logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org")
return
try:
with open('/root/.wpa_sec_uploads', 'r') as f:
ALREADY_UPLOADED = f.read().splitlines()
except OSError:
logging.warning('WPA_SEC: No upload-file found.')
ALREADY_UPLOADED = []
READY = True
def _upload_to_wpasec(path, timeout=30):
"""
Uploads the file to wpa-sec.stanev.org
"""
with open(path, 'rb') as file_to_upload:
headers = {'key': OPTIONS['api_key']}
payload = {'file': file_to_upload}
try:
result = requests.post('https://wpa-sec.stanev.org/?submit',
headers=headers,
files=payload,
timeout=timeout)
if ' already submitted' in result.text:
logging.warning(f"{path} was already submitted.")
except requests.exceptions.RequestException as e:
logging.error(f"WPA_SEC: Got an exception while uploading {path} -> {e}")
raise e
def on_internet_available(display, config, log):
"""
Called in manual mode when there's internet connectivity
"""
if READY:
handshake_dir = config['bettercap']['handshakes']
handshake_filenames = os.listdir(handshake_dir)
handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames]
handshake_new = set(handshake_paths) - set(ALREADY_UPLOADED)
if handshake_new:
logging.info("WPA_SEC: Internet connectivity detected.\
Uploading new handshakes to wpa-sec.stanev.org")
for idx, handshake in enumerate(handshake_new):
display.set('status', f"Uploading handshake to wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})")
display.update(force=True)
try:
_upload_to_wpasec(handshake)
ALREADY_UPLOADED.append(handshake)
with open('/root/.wpa_sec_uploads', 'a') as f:
f.write(handshake + "\n")
logging.info(f"WPA_SEC: Successfuly uploaded {handshake}")
except requests.exceptions.RequestException:
pass
except OSError as os_e:
logging.error(f"WPA_SEC: Got the following error: {os_e}")

View File

@ -3,7 +3,7 @@ from threading import Lock
from PIL import Image
import shutil
import core
import logging
import os
import pwnagotchi, pwnagotchi.plugins as plugins
@ -79,13 +79,12 @@ class Display(View):
self._render_cb = None
self._display = None
self._httpd = None
self.canvas = None
if self._enabled:
self._init_display()
else:
self.on_render(self._on_view_rendered)
core.log("display module is disabled")
logging.warning("display module is disabled")
if self._video_enabled:
_thread.start_new_thread(self._http_serve, ())
@ -93,10 +92,10 @@ class Display(View):
def _http_serve(self):
if self._video_address is not None:
self._httpd = HTTPServer((self._video_address, self._video_port), VideoHandler)
core.log("ui available at http://%s:%d/" % (self._video_address, self._video_port))
logging.info("ui available at http://%s:%d/" % (self._video_address, self._video_port))
self._httpd.serve_forever()
else:
core.log("could not get ip of usb0, video server not starting")
logging.info("could not get ip of usb0, video server not starting")
def _is_inky(self):
return self._display_type in ('inkyphat', 'inky')
@ -104,22 +103,25 @@ class Display(View):
def _is_papirus(self):
return self._display_type in ('papirus', 'papi')
def _is_waveshare1(self):
def _is_waveshare_v1(self):
return self._display_type in ('waveshare_1', 'ws_1', 'waveshare1', 'ws1')
def _is_waveshare2(self):
def _is_waveshare_v2(self):
return self._display_type in ('waveshare_2', 'ws_2', 'waveshare2', 'ws2')
def _is_waveshare(self):
return self._is_waveshare_v1() or self._is_waveshare_v2()
def _init_display(self):
if self._is_inky():
core.log("initializing inky display")
logging.info("initializing inky display")
from inky import InkyPHAT
self._display = InkyPHAT(self._display_color)
self._display.set_border(InkyPHAT.BLACK)
self._render_cb = self._inky_render
elif self._is_papirus():
core.log("initializing papirus display")
logging.info("initializing papirus display")
from pwnagotchi.ui.papirus.epd import EPD
os.environ['EPD_SIZE'] = '2.0'
self._display = EPD()
@ -127,10 +129,9 @@ class Display(View):
self._render_cb = self._papirus_render
elif self._is_waveshare1():
core.log("initializing waveshare v1 display")
logging.info("initializing waveshare v1 display")
if self._display_color == 'black':
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
# core.log("display module started")
self._display = EPD()
self._display.init(self._display.lut_full_update)
self._display.Clear(0xFF)
@ -144,8 +145,8 @@ class Display(View):
self._display.Clear()
self._render_cb = self._waveshare_bc_render
elif self._is_waveshare2():
core.log("initializing waveshare v2 display")
elif self._is_waveshare_v2():
logging.info("initializing waveshare v2 display")
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
self._display = EPD()
self._display.init(self._display.FULL_UPDATE)
@ -154,17 +155,23 @@ class Display(View):
self._render_cb = self._waveshare_render
else:
core.log("unknown display type %s" % self._display_type)
logging.critical("unknown display type %s" % self._display_type)
plugins.on('display_setup', self._display)
self.on_render(self._on_view_rendered)
def image(self):
img = None
if self.canvas is not None:
img = self.canvas if self._rotation == 0 else self.canvas.rotate(-self._rotation)
return img
def clear(self):
if self._display is None:
logging.error("no display object created")
elif self._is_inky():
self._display.Clear()
elif self._is_papirus():
self._display.clear()
elif self._is_waveshare():
self._display.Clear(WHITE)
else:
logging.critical("unknown display type %s" % self._display_type)
def _inky_render(self):
if self._display_color != 'mono':
@ -172,38 +179,40 @@ class Display(View):
else:
display_colors = 2
imgbuf = self.canvas.convert('RGB').convert('P', palette=1, colors=display_colors)
img_buffer = self._canvas.convert('RGB').convert('P', palette=1, colors=display_colors)
if self._display_color == 'red':
imgbuf.putpalette([
img_buffer.putpalette([
255, 255, 255, # index 0 is white
0, 0, 0, # index 1 is black
255, 0, 0 # index 2 is red
])
elif self._display_color == 'yellow':
imgbuf.putpalette([
img_buffer.putpalette([
255, 255, 255, # index 0 is white
0, 0, 0, # index 1 is black
255, 255, 0 # index 2 is yellow
])
else:
imgbuf.putpalette([
img_buffer.putpalette([
255, 255, 255, # index 0 is white
0, 0, 0 # index 1 is black
])
self._display.set_image(imgbuf)
self._display.show()
self._display.set_image(img_buffer)
try:
self._display.show()
except:
print("")
def _papirus_render(self):
self._display.display(self.canvas)
self._display.display(self._canvas)
self._display.partial_update()
def _waveshare_render(self):
buf = self._display.getbuffer(self.canvas)
if self._is_waveshare1():
buf = self._display.getbuffer(self._canvas)
if self._is_waveshare_v1():
self._display.display(buf)
elif self._is_waveshare2():
elif self._is_waveshare_v2():
self._display.displayPartial(buf)
def _waveshare_bc_render(self):
@ -216,13 +225,16 @@ class Display(View):
# Was included in epd2in13bc.py
self._display.displayBlack(buf_black)
def image(self):
img = None
if self._canvas is not None:
img = self._canvas if self._rotation == 0 else self._canvas.rotate(-self._rotation)
return img
def _on_view_rendered(self, img):
# core.log("display::_on_view_rendered")
VideoHandler.render(img)
if self._enabled:
self.canvas = (img if self._rotation == 0 else img.rotate(self._rotation))
self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation))
if self._render_cb is not None:
self._render_cb()

View File

@ -1,9 +1,10 @@
import _thread
from threading import Lock
import time
import logging
from PIL import Image, ImageDraw
import core
import pwnagotchi.utils as utils
import pwnagotchi.plugins as plugins
from pwnagotchi.voice import Voice
@ -94,8 +95,9 @@ class View(object):
'face': Text(value=faces.SLEEP, position=face_pos, color=BLACK, font=fonts.Huge),
'friend_face': Text(value=None, position=(0, 90), font=fonts.Bold, color=BLACK),
'friend_name': Text(value=None, position=(40, 93), font=fonts.BoldSmall, color=BLACK),
'friend_face': Text(value=None, position=(0, (self._height * 0.88) - 15), font=fonts.Bold, color=BLACK),
'friend_name': Text(value=None, position=(40, (self._height * 0.88) - 13), font=fonts.BoldSmall,
color=BLACK),
'name': Text(value='%s>' % 'pwnagotchi', position=name_pos, color=BLACK, font=fonts.Bold),
@ -123,7 +125,7 @@ class View(object):
_thread.start_new_thread(self._refresh_handler, ())
self._ignore_changes = ()
else:
core.log("ui.fps is 0, the display will only update for major changes")
logging.warning("ui.fps is 0, the display will only update for major changes")
self._ignore_changes = ('uptime', 'name')
def add_element(self, key, elem):
@ -144,7 +146,7 @@ class View(object):
def _refresh_handler(self):
delay = 1.0 / self._config['ui']['fps']
# core.log("view refresh handler started with period of %.2fs" % delay)
# logging.info("view refresh handler started with period of %.2fs" % delay)
while True:
name = self._state.get('name')
@ -174,7 +176,7 @@ class View(object):
self.set('channel', '-')
self.set('aps', "%d" % log.associated)
self.set('shakes', '%d (%s)' % (log.handshakes, \
core.total_unique_handshakes(self._config['bettercap']['handshakes'])))
utils.total_unique_handshakes(self._config['bettercap']['handshakes'])))
self.set_closest_peer(log.last_peer)
def is_normal(self):
@ -322,10 +324,10 @@ class View(object):
self.set('status', self._voice.custom(text))
self.update()
def update(self):
def update(self, force=False):
with self._lock:
changes = self._state.changes(ignore=self._ignore_changes)
if len(changes):
if force or len(changes):
self._canvas = Image.new('1', (self._width, self._height), WHITE)
drawer = ImageDraw.Draw(self._canvas)

View File

@ -0,0 +1,98 @@
from datetime import datetime
import logging
import glob
import os
import time
import subprocess
import yaml
# https://stackoverflow.com/questions/823196/yaml-merge-in-python
def merge_config(user, default):
if isinstance(user, dict) and isinstance(default, dict):
for k, v in default.items():
if k not in user:
user[k] = v
else:
user[k] = merge_config(user[k], v)
return user
def load_config(args):
with open(args.config, 'rt') as fp:
config = yaml.safe_load(fp)
if os.path.exists(args.user_config):
with open(args.user_config, 'rt') as fp:
user_config = yaml.safe_load(fp)
config = merge_config(user_config, config)
return config
def setup_logging(args, config):
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
root = logging.getLogger()
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
if config['main']['log']:
file_handler = logging.FileHandler(config['main']['log'])
file_handler.setFormatter(formatter)
root.addHandler(file_handler)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
root.addHandler(console_handler)
def secs_to_hhmmss(secs):
mins, secs = divmod(secs, 60)
hours, mins = divmod(mins, 60)
return '%02d:%02d:%02d' % (hours, mins, secs)
def total_unique_handshakes(path):
expr = os.path.join(path, "*.pcap")
return len(glob.glob(expr))
def iface_channels(ifname):
channels = []
output = subprocess.getoutput("/sbin/iwlist %s freq" % ifname)
for line in output.split("\n"):
line = line.strip()
if line.startswith("Channel "):
channels.append(int(line.split()[1]))
return channels
def led(on=True):
with open('/sys/class/leds/led0/brightness', 'w+t') as fp:
fp.write("%d" % (0 if on is True else 1))
def blink(times=1, delay=0.3):
for t in range(0, times):
led(True)
time.sleep(delay)
led(False)
time.sleep(delay)
led(True)
class StatusFile(object):
def __init__(self, path):
self._path = path
self._updated = None
if os.path.exists(path):
self._updated = datetime.fromtimestamp(os.path.getmtime(path))
def newer_then_days(self, days):
return self._updated is not None and (datetime.now() - self._updated).days < days
def update(self, data=None):
self._updated = datetime.now()
with open(self._path, 'w') as fp:
fp.write(str(self._updated) if data is None else data)

View File

@ -1 +0,0 @@
version = '1.0.0travistest'

View File

@ -138,5 +138,21 @@ class Voice:
associated=log.associated,
handshakes=log.handshakes)
def custom(self, text):
return self._(text)
def hhmmss(self, count, fmt):
if count > 1:
# plural
if fmt == "h":
return self._("hours")
if fmt == "m":
return self._("minutes")
if fmt == "s":
return self._("seconds")
else:
# sing
if fmt == "h":
return self._("hour")
if fmt == "m":
return self._("minute")
if fmt == "s":
return self._("second")
return fmt