mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Merge pull request #341 from fmatray/noai
Add Cache plugin, improve Wigle, debug bt-tether, add more sentences to voices
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,2 @@
|
||||
*.pyc
|
||||
*.pyc
|
||||
.vscode
|
||||
|
@ -33,6 +33,8 @@ main.plugins.bt-tether.dns = "" # optional, default (google): "8.8.8.8 1.1.1.1".
|
||||
|
||||
main.plugins.fix_services.enabled = true
|
||||
|
||||
main.plugins.cache.enabled = true
|
||||
|
||||
main.plugins.gdrivesync.enabled = false
|
||||
main.plugins.gdrivesync.backupfiles = ['']
|
||||
main.plugins.gdrivesync.backup_folder = "PwnagotchiBackups"
|
||||
|
@ -123,6 +123,7 @@ MAC_PTTRN = r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"
|
||||
IP_PTTRN = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
|
||||
DNS_PTTRN = r"^\s*((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*[ ,;]\s*)+((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s*[ ,;]?\s*)$"
|
||||
|
||||
|
||||
class BTTether(plugins.Plugin):
|
||||
__author__ = "Jayofelony, modified my fmatray"
|
||||
__version__ = "1.4"
|
||||
@ -138,13 +139,13 @@ class BTTether(plugins.Plugin):
|
||||
@staticmethod
|
||||
def exec_cmd(cmd, args, pattern=None):
|
||||
try:
|
||||
result = subprocess.run([cmd] + args,
|
||||
check=True, capture_output=True, text=True)
|
||||
result = subprocess.run([cmd] + args, check=True, capture_output=True, text=True)
|
||||
if pattern:
|
||||
return result.stdout.find(pattern)
|
||||
return result
|
||||
except Exception as exp:
|
||||
logging.error(f"[BT-Tether] Error with {cmd} : {exp}")
|
||||
logging.error(f"[BT-Tether] Error with {cmd}")
|
||||
logging.error(f"[BT-Tether] Exception : {exp}")
|
||||
raise exp
|
||||
|
||||
def bluetoothctl(self, args, pattern=None):
|
||||
@ -152,7 +153,7 @@ class BTTether(plugins.Plugin):
|
||||
|
||||
def nmcli(self, args, pattern=None):
|
||||
return self.exec_cmd("nmcli", args, pattern)
|
||||
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("[BT-Tether] plugin loaded.")
|
||||
|
||||
@ -162,7 +163,7 @@ class BTTether(plugins.Plugin):
|
||||
return
|
||||
if not ("mac" in self.options and re.match(MAC_PTTRN, self.options["mac"])):
|
||||
logging.error("[BT-Tether] Error with mac address")
|
||||
return
|
||||
return
|
||||
|
||||
if not ("phone" in self.options and self.options["phone"].lower() in ["android", "ios"]):
|
||||
logging.error("[BT-Tether] Phone type not supported")
|
||||
@ -181,23 +182,36 @@ class BTTether(plugins.Plugin):
|
||||
self.mac = self.options["mac"]
|
||||
dns = self.options.get("dns", "8.8.8.8 1.1.1.1")
|
||||
if not re.match(DNS_PTTRN, dns):
|
||||
logging.error(f"[BT-Tether] DNS error: {dns}")
|
||||
if dns == "":
|
||||
logging.error(f"[BT-Tether] Empty DNS setting")
|
||||
else:
|
||||
logging.error(f"[BT-Tether] Wrong DNS setting: '{dns}'")
|
||||
return
|
||||
dns = re.sub("[\s,;]+", " ", dns).strip() # DNS cleaning
|
||||
dns = re.sub("[\s,;]+", " ", dns).strip() # DNS cleaning
|
||||
|
||||
try:
|
||||
# Configure connection. Metric is set to 200 to prefer connection over USB
|
||||
self.nmcli(["connection", "modify", f"{self.phone_name}",
|
||||
"connection.type", "bluetooth",
|
||||
"bluetooth.type", "panu",
|
||||
"bluetooth.bdaddr", f"{self.mac}",
|
||||
"connection.autoconnect", "yes",
|
||||
"connection.autoconnect-retries", "0",
|
||||
"ipv4.method", "manual",
|
||||
"ipv4.dns", f"{dns}",
|
||||
"ipv4.addresses", f"{address}/24",
|
||||
"ipv4.gateway", f"{gateway}",
|
||||
"ipv4.route-metric", "200" ])
|
||||
self.nmcli(
|
||||
[
|
||||
"connection", "modify", f"{self.phone_name}",
|
||||
"connection.type", "bluetooth",
|
||||
"bluetooth.type", "panu",
|
||||
"bluetooth.bdaddr", f"{self.mac}",
|
||||
"connection.autoconnect", "yes",
|
||||
"connection.autoconnect-retries", "0",
|
||||
"ipv4.method", "manual",
|
||||
"ipv4.dns", f"{dns}",
|
||||
"ipv4.addresses", f"{address}/24",
|
||||
"ipv4.gateway", f"{gateway}",
|
||||
"ipv4.route-metric", "200",
|
||||
]
|
||||
)
|
||||
# Configure Device to autoconnect
|
||||
self.nmcli([
|
||||
"device", "set", f"{self.mac}",
|
||||
"autoconnect", "yes",
|
||||
"managed", "yes"
|
||||
])
|
||||
self.nmcli(["connection", "reload"])
|
||||
self.ready = True
|
||||
logging.info(f"[BT-Tether] Connection {self.phone_name} configured")
|
||||
@ -205,7 +219,7 @@ class BTTether(plugins.Plugin):
|
||||
logging.error(f"[BT-Tether] Error while configuring: {e}")
|
||||
return
|
||||
try:
|
||||
time.sleep(5) # Give some delay to configure before going up
|
||||
time.sleep(5) # Give some delay to configure before going up
|
||||
self.nmcli(["connection", "up", f"{self.phone_name}"])
|
||||
except Exception as e:
|
||||
logging.error(f"[BT-Tether] Failed to connect to device: {e}")
|
||||
@ -230,9 +244,18 @@ class BTTether(plugins.Plugin):
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
with ui._lock:
|
||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-',
|
||||
position=(ui.width() / 2 - 10, 0),
|
||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
ui.add_element(
|
||||
"bluetooth",
|
||||
LabeledValue(
|
||||
color=BLACK,
|
||||
label="BT",
|
||||
value="-",
|
||||
position=(ui.width() / 2 - 10, 0),
|
||||
label_font=fonts.Bold,
|
||||
text_font=fonts.Medium,
|
||||
),
|
||||
)
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if not self.ready:
|
||||
return
|
||||
@ -240,17 +263,26 @@ class BTTether(plugins.Plugin):
|
||||
status = ""
|
||||
try:
|
||||
# Checking connection
|
||||
if self.nmcli(["-w", "0", "-g", "GENERAL.STATE", "connection", "show", self.phone_name],
|
||||
"activated") != -1:
|
||||
if (
|
||||
self.nmcli(["-w", "0", "-g", "GENERAL.STATE", "connection", "show", self.phone_name],
|
||||
"activated",
|
||||
)
|
||||
!= -1
|
||||
):
|
||||
ui.set("bluetooth", "U")
|
||||
return
|
||||
else:
|
||||
ui.set("bluetooth", "D")
|
||||
status = "BT Conn. down"
|
||||
|
||||
|
||||
# Checking device
|
||||
if self.nmcli(["-w", "0", "-g", "GENERAL.STATE", "device", "show", self.mac],
|
||||
"(connected)") != -1:
|
||||
if (
|
||||
self.nmcli(
|
||||
["-w", "0", "-g", "GENERAL.STATE", "device", "show", self.mac],
|
||||
"(connected)",
|
||||
)
|
||||
!= -1
|
||||
):
|
||||
ui.set("bluetooth", "C")
|
||||
status += "\nBT dev conn."
|
||||
else:
|
||||
@ -269,26 +301,28 @@ class BTTether(plugins.Plugin):
|
||||
if path == "/" or not path:
|
||||
try:
|
||||
bluetooth = self.bluetoothctl(["info", self.mac])
|
||||
bluetooth = bluetooth.stdout.replace('\n', '<br>')
|
||||
bluetooth = bluetooth.stdout.replace("\n", "<br>")
|
||||
except Exception as e:
|
||||
bluetooth = "Error while checking bluetoothctl"
|
||||
|
||||
try:
|
||||
device = self.nmcli(["-w", "0","device", "show", self.mac])
|
||||
device = device.stdout.replace('\n', '<br>')
|
||||
|
||||
try:
|
||||
device = self.nmcli(["-w", "0", "device", "show", self.mac])
|
||||
device = device.stdout.replace("\n", "<br>")
|
||||
except Exception as e:
|
||||
device = "Error while checking nmcli device"
|
||||
|
||||
|
||||
try:
|
||||
connection = self.nmcli(["-w", "0","connection", "show", self.phone_name])
|
||||
connection = connection.stdout.replace('\n', '<br>')
|
||||
connection = self.nmcli(["-w", "0", "connection", "show", self.phone_name])
|
||||
connection = connection.stdout.replace("\n", "<br>")
|
||||
except Exception as e:
|
||||
connection = "Error while checking nmcli connection"
|
||||
|
||||
logging.debug(device)
|
||||
return render_template_string(TEMPLATE,
|
||||
title="BT-Tether",
|
||||
bluetooth=bluetooth,
|
||||
device=device,
|
||||
connection=connection)
|
||||
abort(404)
|
||||
return render_template_string(
|
||||
TEMPLATE,
|
||||
title="BT-Tether",
|
||||
bluetooth=bluetooth,
|
||||
device=device,
|
||||
connection=connection,
|
||||
)
|
||||
abort(404)
|
||||
|
119
pwnagotchi/plugins/default/cache.py
Normal file
119
pwnagotchi/plugins/default/cache.py
Normal file
@ -0,0 +1,119 @@
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import pathlib
|
||||
import pwnagotchi.plugins as plugins
|
||||
from datetime import datetime, UTC
|
||||
from threading import Lock
|
||||
|
||||
|
||||
def read_ap_cache(cache_dir, file):
|
||||
cache_filename = os.path.basename(re.sub(r"\.(pcap|gps\.json|geo\.json)$", ".cache", file))
|
||||
cache_filename = os.path.join(cache_dir, cache_filename)
|
||||
if not os.path.exists(cache_filename):
|
||||
logging.info("Cache not exist")
|
||||
return None
|
||||
try:
|
||||
with open(cache_filename, "r") as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
logging.info(f"Exception {e}")
|
||||
return None
|
||||
|
||||
|
||||
class Cache(plugins.Plugin):
|
||||
__author__ = "fmatray"
|
||||
__version__ = "1.0.0"
|
||||
__license__ = "GPL3"
|
||||
__description__ = "A simple plugin to cache AP informations"
|
||||
|
||||
def __init__(self):
|
||||
self.options = dict()
|
||||
self.ready = False
|
||||
self.lock = Lock()
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("[CACHE] plugin loaded.")
|
||||
|
||||
def on_config_changed(self, config):
|
||||
try:
|
||||
handshake_dir = config["bettercap"].get("handshakes")
|
||||
self.cache_dir = os.path.join(handshake_dir, "cache")
|
||||
os.makedirs(self.cache_dir, exist_ok=True)
|
||||
except Exception:
|
||||
logging.info(f"[CACHE] Cannot access to the cache directory")
|
||||
return
|
||||
self.last_clean = datetime.now(tz=UTC)
|
||||
self.ready = True
|
||||
logging.info(f"[CACHE] Cache plugin configured")
|
||||
self.clean_ap_cache()
|
||||
|
||||
def on_unload(self, ui):
|
||||
self.clean_ap_cache()
|
||||
|
||||
def clean_ap_cache(self):
|
||||
if not self.ready:
|
||||
return
|
||||
with self.lock:
|
||||
ctime = datetime.now(tz=UTC)
|
||||
cache_to_delete = list()
|
||||
for cache_file in pathlib.Path(self.cache_dir).glob("*.apcache"):
|
||||
try:
|
||||
mtime = datetime.fromtimestamp(cache_file.lstat().st_mtime, tz=UTC)
|
||||
if (ctime - mtime).total_seconds() > 60 * 5:
|
||||
cache_to_delete.append(cache_file)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
if cache_to_delete:
|
||||
logging.info(f"[CACHE] Cleaning {len(cache_to_delete)} files")
|
||||
for cache_file in cache_to_delete:
|
||||
try:
|
||||
cache_file.unlink()
|
||||
except FileNotFoundError as e:
|
||||
pass
|
||||
|
||||
def write_ap_cache(self, access_point):
|
||||
with self.lock:
|
||||
try:
|
||||
mac = access_point["mac"].replace(":", "")
|
||||
hostname = re.sub(r"[^a-zA-Z0-9]", "", access_point["hostname"])
|
||||
except KeyError:
|
||||
return
|
||||
cache_file = os.path.join(self.cache_dir, f"{hostname}_{mac}.apcache")
|
||||
try:
|
||||
with open(cache_file, "w") as f:
|
||||
json.dump(access_point, f)
|
||||
except Exception as e:
|
||||
logging.error(f"[CACHE] Cannot write {cache_file}: {e}")
|
||||
pass
|
||||
|
||||
def on_wifi_update(self, agent, access_points):
|
||||
if self.ready:
|
||||
for ap in filter(lambda ap: ap["hostname"] not in ["", "<hidden>"], access_points):
|
||||
self.write_ap_cache(ap)
|
||||
|
||||
def on_unfiltered_ap_list(self, agent, aps):
|
||||
if self.ready:
|
||||
for ap in filter(lambda ap: ap["hostname"] not in ["", "<hidden>"], aps):
|
||||
self.write_ap_cache(ap)
|
||||
|
||||
def on_association(self, agent, access_point):
|
||||
if self.ready:
|
||||
self.write_ap_cache(access_point)
|
||||
|
||||
def on_deauthentication(self, agent, access_point, client_station):
|
||||
if self.ready:
|
||||
self.write_ap_cache(access_point)
|
||||
|
||||
def on_handshake(self, agent, filename, access_point, client_station):
|
||||
if self.ready:
|
||||
self.write_ap_cache(access_point)
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if not self.ready:
|
||||
return
|
||||
current_time = datetime.now(tz=UTC)
|
||||
if (current_time - self.last_clean).total_seconds() > 60:
|
||||
self.clean_ap_cache()
|
||||
self.last_clean = current_time
|
@ -9,6 +9,7 @@ from glob import glob
|
||||
from threading import Lock
|
||||
from io import StringIO
|
||||
from datetime import datetime, UTC
|
||||
from dataclasses import dataclass
|
||||
|
||||
from flask import make_response, redirect
|
||||
from pwnagotchi.utils import (
|
||||
@ -19,6 +20,7 @@ from pwnagotchi.utils import (
|
||||
remove_whitelisted,
|
||||
)
|
||||
from pwnagotchi import plugins
|
||||
from pwnagotchi.plugins.default.cache import read_ap_cache
|
||||
from pwnagotchi._version import __version__ as __pwnagotchi_version__
|
||||
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
@ -28,9 +30,42 @@ from pwnagotchi.ui.view import BLACK
|
||||
from scapy.all import Scapy_Exception
|
||||
|
||||
|
||||
@dataclass
|
||||
class WigleStatistics:
|
||||
ready: bool = False
|
||||
username: str = None
|
||||
rank: int = None
|
||||
monthrank: int = None
|
||||
discoveredwiFi: int = None
|
||||
last: str = None
|
||||
groupID: str = None
|
||||
groupname: str = None
|
||||
grouprank: int = None
|
||||
|
||||
def update_user(self, json_res):
|
||||
self.ready = True
|
||||
self.username = json_res["user"]
|
||||
self.rank = json_res["rank"]
|
||||
self.monthrank = json_res["monthRank"]
|
||||
self.discoveredwiFi = json_res["statistics"]["discoveredWiFi"]
|
||||
last = json_res["statistics"]["last"]
|
||||
self.last = f"{last[6:8]}/{last[4:6]}/{last[0:4]}"
|
||||
|
||||
def update_user_group(self, json_res):
|
||||
self.groupID = json_res["groupId"]
|
||||
self.groupname = json_res["groupName"]
|
||||
|
||||
def update_group(self, json_res):
|
||||
rank = 1
|
||||
for group in json_res["groups"]:
|
||||
if group["groupId"] == self.groupID:
|
||||
self.grouprank = rank
|
||||
rank += 1
|
||||
|
||||
|
||||
class Wigle(plugins.Plugin):
|
||||
__author__ = "Dadav and updated by Jayofelony and fmatray"
|
||||
__version__ = "4.0.0"
|
||||
__version__ = "4.1.0"
|
||||
__license__ = "GPL3"
|
||||
__description__ = "This plugin automatically uploads collected WiFi to wigle.net"
|
||||
LABEL_SPACING = 0
|
||||
@ -41,17 +76,13 @@ class Wigle(plugins.Plugin):
|
||||
self.skip = list()
|
||||
self.lock = Lock()
|
||||
self.options = dict()
|
||||
self.statistics = dict(
|
||||
ready=False,
|
||||
username=None,
|
||||
rank=None,
|
||||
monthrank=None,
|
||||
discoveredwiFi=None,
|
||||
last=None,
|
||||
)
|
||||
self.statistics = WigleStatistics()
|
||||
self.last_stat = datetime.now(tz=UTC)
|
||||
self.ui_counter = 0
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("[WIGLE] plugin loaded.")
|
||||
|
||||
def on_config_changed(self, config):
|
||||
self.api_key = self.options.get("api_key", None)
|
||||
if not self.api_key:
|
||||
@ -61,6 +92,7 @@ class Wigle(plugins.Plugin):
|
||||
self.handshake_dir = config["bettercap"].get("handshakes")
|
||||
report_filename = os.path.join(self.handshake_dir, ".wigle_uploads")
|
||||
self.report = StatusFile(report_filename, data_format="json")
|
||||
self.cache_dir = os.path.join(self.handshake_dir, "cache")
|
||||
self.cvs_dir = self.options.get("cvs_dir", None)
|
||||
self.whitelist = config["main"].get("whitelist", [])
|
||||
self.timeout = self.options.get("timeout", 30)
|
||||
@ -120,8 +152,20 @@ class Wigle(plugins.Plugin):
|
||||
return None
|
||||
return gps_data
|
||||
|
||||
@staticmethod
|
||||
def get_pcap_data(pcap_filename):
|
||||
def get_pcap_data(self, pcap_filename):
|
||||
try:
|
||||
if cache := read_ap_cache(self.cache_dir, self.pcap_filename):
|
||||
logging.info(f"[WIGLE] Using cache for {pcap_filename}")
|
||||
return {
|
||||
WifiInfo.BSSID: cache["mac"],
|
||||
WifiInfo.ESSID: cache["hostname"],
|
||||
WifiInfo.ENCRYPTION: cache["encryption"],
|
||||
WifiInfo.CHANNEL: cache["channel"],
|
||||
WifiInfo.FREQUENCY: cache["frequency"],
|
||||
WifiInfo.RSSI: cache["rssi"],
|
||||
}
|
||||
except (AttributeError, KeyError):
|
||||
pass
|
||||
try:
|
||||
pcap_data = extract_from_pcap(
|
||||
pcap_filename,
|
||||
@ -154,14 +198,16 @@ class Wigle(plugins.Plugin):
|
||||
f"device={pwnagotchi.name()},display=kismet,board=RaspberryPi,brand=pwnagotchi,star=Sol,body=3,subBody=0\n"
|
||||
f"MAC,SSID,AuthMode,FirstSeen,Channel,Frequency,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,RCOIs,MfgrId,Type\n"
|
||||
)
|
||||
writer = csv.writer(
|
||||
content, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\"
|
||||
)
|
||||
writer = csv.writer(content, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\")
|
||||
for gps_data, pcap_data in data: # write WIFIs
|
||||
try:
|
||||
timestamp = datetime.strptime(gps_data["Updated"].rsplit(".")[0], "%Y-%m-%dT%H:%M:%S").strftime("%Y-%m-%d %H:%M:%S")
|
||||
timestamp = datetime.strptime(
|
||||
gps_data["Updated"].rsplit(".")[0], "%Y-%m-%dT%H:%M:%S"
|
||||
).strftime("%Y-%m-%d %H:%M:%S")
|
||||
except ValueError:
|
||||
timestamp = datetime.strptime(gps_data["Updated"].rsplit(".")[0], "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%d %H:%M:%S")
|
||||
timestamp = datetime.strptime(
|
||||
gps_data["Updated"].rsplit(".")[0], "%Y-%m-%d %H:%M:%S"
|
||||
).strftime("%Y-%m-%d %H:%M:%S")
|
||||
writer.writerow(
|
||||
[
|
||||
pcap_data[WifiInfo.BSSID],
|
||||
@ -238,31 +284,46 @@ class Wigle(plugins.Plugin):
|
||||
self.post_wigle(reported, cvs_filename, cvs_content, no_err_entries)
|
||||
display.on_normal()
|
||||
|
||||
def get_statistics(self, force=False):
|
||||
if not force and (datetime.now(tz=UTC) - self.last_stat).total_seconds() < 30:
|
||||
return
|
||||
self.last_stat = datetime.now(tz=UTC)
|
||||
def request_statistics(self, url):
|
||||
try:
|
||||
self.statistics["ready"] = False
|
||||
json_res = requests.get(
|
||||
"https://api.wigle.net/api/v2/stats/user",
|
||||
return requests.get(
|
||||
url,
|
||||
headers={
|
||||
"Authorization": f"Basic {self.api_key}",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
timeout=self.timeout,
|
||||
).json()
|
||||
if not json_res["success"]:
|
||||
return
|
||||
self.statistics["ready"] = True
|
||||
self.statistics["username"] = json_res["user"]
|
||||
self.statistics["rank"] = json_res["rank"]
|
||||
self.statistics["monthrank"] = json_res["monthRank"]
|
||||
self.statistics["discoveredwiFi"] = json_res["statistics"]["discoveredWiFi"]
|
||||
last = json_res["statistics"]["last"]
|
||||
self.statistics["last"] = f"{last[6:8]}/{last[4:6]}/{last[0:4]}"
|
||||
except (requests.exceptions.RequestException, OSError) as exp:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_user_statistics(self):
|
||||
json_res = self.request_statistics(
|
||||
"https://api.wigle.net/api/v2/stats/user",
|
||||
)
|
||||
if json_res and json_res["success"]:
|
||||
self.statistics.update_user(json_res)
|
||||
|
||||
def get_usergroup_statistics(self):
|
||||
if not self.statistics.username or self.statistics.groupID:
|
||||
return
|
||||
url = f"https://api.wigle.net/api/v2/group/groupForUser/{self.statistics.username}"
|
||||
if json_res := self.request_statistics(url):
|
||||
self.statistics.update_user_group(json_res)
|
||||
|
||||
def get_group_statistics(self):
|
||||
if not self.statistics.groupID:
|
||||
return
|
||||
json_res = self.request_statistics("https://api.wigle.net/api/v2/stats/group")
|
||||
if json_res and json_res["success"]:
|
||||
self.statistics.update_group(json_res)
|
||||
|
||||
def get_statistics(self, force=False):
|
||||
if force or (datetime.now(tz=UTC) - self.last_stat).total_seconds() > 30:
|
||||
self.last_stat = datetime.now(tz=UTC)
|
||||
self.get_user_statistics()
|
||||
self.get_usergroup_statistics()
|
||||
self.get_group_statistics()
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
if not self.ready:
|
||||
@ -283,23 +344,28 @@ class Wigle(plugins.Plugin):
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
ui.remove_element("wigle")
|
||||
try:
|
||||
ui.remove_element("wigle")
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if not self.ready:
|
||||
return
|
||||
with ui._lock:
|
||||
if not self.statistics["ready"]:
|
||||
if not (self.ready and self.statistics.ready):
|
||||
ui.set("wigle", "We Will Wait Wigle")
|
||||
return
|
||||
msg = "-"
|
||||
self.ui_counter = (self.ui_counter + 1) % 4
|
||||
self.ui_counter = (self.ui_counter + 1) % 6
|
||||
if self.ui_counter == 0:
|
||||
msg = f"User:{self.statistics['username']}"
|
||||
msg = f"User:{self.statistics.username}"
|
||||
if self.ui_counter == 1:
|
||||
msg = f"Rank:{self.statistics['rank']} Month:{self.statistics['monthrank']}"
|
||||
msg = f"Rank:{self.statistics.rank} Month:{self.statistics.monthrank}"
|
||||
elif self.ui_counter == 2:
|
||||
msg = f"{self.statistics['discoveredwiFi']} discovered WiFis"
|
||||
msg = f"{self.statistics.discoveredwiFi} discovered WiFis"
|
||||
elif self.ui_counter == 3:
|
||||
msg = f"Last upl.:{self.statistics['last']}"
|
||||
msg = f"Last upl.:{self.statistics.last}"
|
||||
elif self.ui_counter == 4:
|
||||
msg = f"Grp:{self.statistics.groupname}"
|
||||
elif self.ui_counter == 5:
|
||||
msg = f"Grp rank:{self.statistics.grouprank}"
|
||||
ui.set("wigle", msg)
|
||||
|
@ -27,11 +27,19 @@ class Voice:
|
||||
self._('Hack the Planet!'),
|
||||
self._('No more mister Wi-Fi!!'),
|
||||
self._('Pretty fly 4 a Wi-Fi!'),
|
||||
self._('Good Pwning!'), # Battlestar Galactica
|
||||
self._('Ensign, Engage!'), # Star trek
|
||||
self._('Free your Wi-Fi!'), # Matrix
|
||||
self._('Chevron Seven, locked.'), # Stargate
|
||||
self._('May the Wi-fi be with you'), # Star wars
|
||||
])
|
||||
|
||||
def on_keys_generation(self):
|
||||
return random.choice([
|
||||
self._('Generating keys, do not turn off ...')])
|
||||
self._('Generating keys, do not turn off ...'),
|
||||
self._('Are you the keymaster?'), # Ghostbusters
|
||||
self._('I am the keymaster!'), # Ghostbusters
|
||||
])
|
||||
|
||||
def on_normal(self):
|
||||
return random.choice([
|
||||
@ -44,8 +52,7 @@ class Voice:
|
||||
def on_reading_logs(self, lines_so_far=0):
|
||||
if lines_so_far == 0:
|
||||
return self._('Reading last session logs ...')
|
||||
else:
|
||||
return self._('Read {lines_so_far} log lines so far ...').format(lines_so_far=lines_so_far)
|
||||
return self._('Read {lines_so_far} log lines so far ...').format(lines_so_far=lines_so_far)
|
||||
|
||||
def on_bored(self):
|
||||
return random.choice([
|
||||
@ -53,7 +60,11 @@ class Voice:
|
||||
self._('Let\'s go for a walk!')])
|
||||
|
||||
def on_motivated(self, reward):
|
||||
return self._('This is the best day of my life!')
|
||||
return random.choice([
|
||||
self._('This is the best day of my life!'),
|
||||
self._('All your base are belong to us'),
|
||||
self._('Fascinating!'), # Star trek
|
||||
])
|
||||
|
||||
def on_demotivated(self, reward):
|
||||
return self._('Shitty day :/')
|
||||
@ -63,6 +74,8 @@ class Voice:
|
||||
self._('I\'m extremely bored ...'),
|
||||
self._('I\'m very sad ...'),
|
||||
self._('I\'m sad'),
|
||||
self._('I\'m so happy ...'), # Marvin in H2G2
|
||||
self._('Life? Don\'t talk to me about life.'), # Also Marvin in H2G2
|
||||
'...'])
|
||||
|
||||
def on_angry(self):
|
||||
@ -78,17 +91,17 @@ class Voice:
|
||||
self._('I pwn therefore I am.'),
|
||||
self._('So many networks!!!'),
|
||||
self._('I\'m having so much fun!'),
|
||||
self._('It\'s a Wi-Fi system! I know this!'), # Jurassic park
|
||||
self._('My crime is that of curiosity ...')])
|
||||
|
||||
def on_new_peer(self, peer):
|
||||
if peer.first_encounter():
|
||||
return random.choice([
|
||||
self._('Hello {name}! Nice to meet you.').format(name=peer.name())])
|
||||
else:
|
||||
return random.choice([
|
||||
self._('Yo {name}! Sup?').format(name=peer.name()),
|
||||
self._('Hey {name} how are you doing?').format(name=peer.name()),
|
||||
self._('Unit {name} is nearby!').format(name=peer.name())])
|
||||
return random.choice([
|
||||
self._('Yo {name}! Sup?').format(name=peer.name()),
|
||||
self._('Hey {name} how are you doing?').format(name=peer.name()),
|
||||
self._('Unit {name} is nearby!').format(name=peer.name())])
|
||||
|
||||
def on_lost_peer(self, peer):
|
||||
return random.choice([
|
||||
@ -104,19 +117,23 @@ class Voice:
|
||||
def on_grateful(self):
|
||||
return random.choice([
|
||||
self._('Good friends are a blessing!'),
|
||||
self._('I love my friends!')])
|
||||
self._('I love my friends!')
|
||||
])
|
||||
|
||||
def on_lonely(self):
|
||||
return random.choice([
|
||||
self._('Nobody wants to play with me ...'),
|
||||
self._('I feel so alone ...'),
|
||||
self._('Let\'s find friends'),
|
||||
self._('Where\'s everybody?!')])
|
||||
|
||||
def on_napping(self, secs):
|
||||
return random.choice([
|
||||
self._('Napping for {secs}s ...').format(secs=secs),
|
||||
self._('Zzzzz'),
|
||||
self._('ZzzZzzz ({secs}s)').format(secs=secs)])
|
||||
self._('Snoring ...'),
|
||||
self._('ZzzZzzz ({secs}s)').format(secs=secs),
|
||||
])
|
||||
|
||||
def on_shutdown(self):
|
||||
return random.choice([
|
||||
@ -124,12 +141,17 @@ class Voice:
|
||||
self._('Zzz')])
|
||||
|
||||
def on_awakening(self):
|
||||
return random.choice(['...', '!'])
|
||||
return random.choice([
|
||||
'...',
|
||||
'!',
|
||||
'Hello World!',
|
||||
self._('I dreamed of electric sheep'),
|
||||
])
|
||||
|
||||
def on_waiting(self, secs):
|
||||
return random.choice([
|
||||
self._('Waiting for {secs}s ...').format(secs=secs),
|
||||
'...',
|
||||
self._('Waiting for {secs}s ...').format(secs=secs),
|
||||
self._('Looking around ({secs}s)').format(secs=secs)])
|
||||
|
||||
def on_assoc(self, ap):
|
||||
@ -138,12 +160,16 @@ class Voice:
|
||||
return random.choice([
|
||||
self._('Hey {what} let\'s be friends!').format(what=what),
|
||||
self._('Associating to {what}').format(what=what),
|
||||
self._('Yo {what}!').format(what=what)])
|
||||
self._('Yo {what}!').format(what=what),
|
||||
self._('Rise and Shine Mr. {what}!').format(what=what), # Half Life
|
||||
])
|
||||
|
||||
def on_deauth(self, sta):
|
||||
return random.choice([
|
||||
self._('Just decided that {mac} needs no WiFi!').format(mac=sta['mac']),
|
||||
self._('Just decided that {mac} needs no Wi-Fi!').format(mac=sta['mac']),
|
||||
self._('Deauthenticating {mac}').format(mac=sta['mac']),
|
||||
self._('No more Wi-Fi for {mac}').format(mac=sta['mac']),
|
||||
self._('It\'s a trap! {mac}').format(mac=sta['mac']), # Star wars
|
||||
self._('Kickbanning {mac}!').format(mac=sta['mac'])])
|
||||
|
||||
def on_handshakes(self, new_shakes):
|
||||
@ -155,10 +181,19 @@ class Voice:
|
||||
return self._('You have {count} new message{plural}!').format(count=count, plural=s)
|
||||
|
||||
def on_rebooting(self):
|
||||
return self._("Oops, something went wrong ... Rebooting ...")
|
||||
return random.choice([
|
||||
self._("Oops, something went wrong ... Rebooting ..."),
|
||||
self._("Have you tried turning it off and on again?"), # The IT crew
|
||||
self._("I\'m afraid Dave"), # 2001 Space Odyssey
|
||||
self._("I\'m dead, Jim!"), # Star Trek
|
||||
self._("I have a bad feeling about this"), # Star wars
|
||||
])
|
||||
|
||||
def on_uploading(self, to):
|
||||
return self._("Uploading data to {to} ...").format(to=to)
|
||||
return random.choice([
|
||||
self._("Uploading data to {to} ...").format(to=to),
|
||||
self._("Beam me up to {to}").format(to=to),
|
||||
])
|
||||
|
||||
def on_downloading(self, name):
|
||||
return self._("Downloading from {name} ...").format(name=name)
|
||||
|
Reference in New Issue
Block a user