diff --git a/.DEREK.yml b/.DEREK.yml
index c63d9d15..9598f24d 100644
--- a/.DEREK.yml
+++ b/.DEREK.yml
@@ -3,6 +3,7 @@ maintainers:
- caquino
- dadav
- justin-p
+ - hexwaxwing
features:
- comments
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..047f1e29
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at pwnagotchi@gmail.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/README.md b/README.md
index 9a918906..8ed53696 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
+
@@ -40,7 +41,4 @@ For hackers to learn reinforcement learning, WiFi networking and have an excuse
## License
-`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license.
-
-
-
+`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license.
\ No newline at end of file
diff --git a/builder/pwnagotchi.yml b/builder/pwnagotchi.yml
index 515ee705..a8dca14b 100644
--- a/builder/pwnagotchi.yml
+++ b/builder/pwnagotchi.yml
@@ -3,28 +3,135 @@
- 127.0.0.1
become: yes
vars:
- pwn_hostname: "pwnagotchi"
- pwn_version: "master"
+ pwnagotchi:
+ hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}"
+ version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }} "
+ system:
+ boot_options:
+ - "dtoverlay=dwc2"
+ - "dtparam=spi=on"
+ - "dtoverlay=spi1-3cs"
+ - "dtoverlay=pi3-disable-bt"
+ - "dtparam=audio=off"
+ services:
+ enable:
+ - dphys-swapfile.service
+ - getty@ttyGS0.service
+ disable:
+ - apt-daily.timer
+ - apt-daily.service
+ - apt-daily-upgrade.timer
+ - apt-daily-upgrade.service
+ - wpa_supplicant.service
+ - bluetooth.service
+ - triggerhappy.service
+ - ifup@wlan0.service
+ packages:
+ pip:
+ install:
+ - inky
+ - smbus2
+ - absl-py>=0.1.6
+ - enum34
+ - gast==0.2.2
+ - google_pasta
+ - opt_einsum
+ - scapy
+ - gym
+ - keras_applications>=1.0.6
+ - keras_preprocessing>=1.0.5
+ - stable-baselines
+ - file_read_backwards
+ - tensorflow_estimator>=1.14.0,<1.15.0
+ - tensorboard>=1.13.0,<1.14.0
+ apt:
+ remove:
+ - rasberrypi-net-mods
+ - dhcpcd5
+ - triggerhappy
+ - wpa_supplicant
+ - nfs-common
+ install:
+ - vim
+ - screen
+ - golang
+ - git
+ - build-essential
+ - python3-pip
+ - unzip
+ - gawk
+ - libopenmpi-dev
+ - libatlas-base-dev
+ - libjasper-dev
+ - libqtgui4
+ - libqt4-test
+ - libopenjp2-7
+ - tcpdump
+ - lsof
+ - libilmbase23
+ - libopenexr23
+ - libgstreamer1.0-0
+ - libavcodec58
+ - libavformat58
+ - libswscale5
+ - libpcap-dev
+ - libusb-1.0-0-dev
+ - libnetfilter-queue-dev
+ - dphys-swapfile
+ - kalipi-kernel
+ - kalipi-bootloader
+ - kalipi-re4son-firmware
+ - kalipi-kernel-headers
+ - libraspberrypi0
+ - libraspberrypi-dev
+ - libraspberrypi-doc
+ - libraspberrypi-bin
+ - fonts-dejavu
+ - fonts-dejavu-core
+ - fonts-dejavu-extra
+ - python3-crypto
+ - python3-requests
+ - python3-yaml
+ - python3-smbus
+ - python3-inkyphat
+ - python3-numpy
+ - python3-pil
+ - python3-tweepy
+ - python3-opencv
+ - python3-termcolor
+ - python3-astor
+ - python3-backports.weakref
+ - python3-h5py
+ - python3-six
+ - python3-protobuf
+ - python3-wrapt
+ - python3-wheel
+ - python3-mock
+ - python3-scipy
+ - python3-cloudpickle
+
+ bettercap:
+ query: "assets[?contains(name, 'armv6l')].browser_download_url"
tasks:
- name: selected hostname
debug:
- msg: "{{ pwn_hostname }}"
+ msg: "{{ pwnagotchi.hostname }}"
- name: build version
debug:
- msg: "{{ pwn_version }}"
+ msg: "{{ pwnagotchi.version }}"
- name: change hostname
hostname:
- name: "{{pwn_hostname}}"
+ name: "{{pwnagotchi.hostname}}"
- name: add hostname to /etc/hosts
lineinfile:
dest: /etc/hosts
regexp: '^127\.0\.0\.1[ \t]+localhost'
- line: '127.0.0.1 localhost {{pwn_hostname}} {{pwn_hostname}}.local'
+ line: '127.0.0.1 localhost {{pwnagotchi.hostname}} {{pwnagotchi.hostname}}.local'
state: present
- name: Add re4son-kernel repo key
@@ -41,88 +148,55 @@
apt:
update_cache: yes
+ - name: remove unecessary apt packages
+ apt:
+ name: "{{ packages.apt.remove }}"
+ state: absent
+ purge: yes
+
- name: upgrade apt distro
apt:
upgrade: dist
- name: install packages
apt:
- name: "{{ packages }}"
+ name: "{{ packages.apt.install }}"
state: present
- vars:
- packages:
- - vim
- - screen
- - golang
- - git
- - build-essential
- - python3-pip
- - gawk
- - libopenmpi-dev
- - libatlas-base-dev
- - libjasper-dev
- - libqtgui4
- - libqt4-test
- - libopenjp2-7
- - tcpdump
- - lsof
- - libilmbase23
- - libopenexr23
- - libgstreamer1.0-0
- - libavcodec58
- - libavformat58
- - libswscale5
- - libpcap-dev
- - libusb-1.0-0-dev
- - libnetfilter-queue-dev
- - dphys-swapfile
- - kalipi-kernel
- - kalipi-bootloader
- - kalipi-re4son-firmware
- - kalipi-kernel-headers
- - libraspberrypi0
- - libraspberrypi-dev
- - libraspberrypi-doc
- - libraspberrypi-bin
- - fonts-dejavu
- - fonts-dejavu-core
- - fonts-dejavu-extra
- name: configure dphys-swapfile
file:
path: /etc/dphys-swapfile
content: "CONF_SWAPSIZE=1024"
- - name: disable unecessary services
- systemd:
- name: "{{services}}"
- state: stopped
- enabled: no
- vars:
- services:
- - apt-daily.timer
- - apt-daily.service
- - apt-daily-upgrade.timer
- - apt-daily-upgrade.service
- - bluetooth.service
- - triggerhappy.service
+ - name: acquire python3 pip target
+ command: "python3 -c 'import sys;print(sys.path.pop())'"
+ register: pip_target
- - name: enable dphys-swapfile service
- systemd:
- name: dphys-swapfile.service
- state: started
- enabled: yes
+ - name: install pip packages
+ pip:
+ name: "{{packages.pip.install}}"
+ extra_args: "--no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --prefer-binary --no-cache-dir --platform=armv6l --target={{ pip_target.stdout }}"
- - name: build bettercap
- command: go get -u github.com/bettercap/bettercap
- environment:
- GOPATH: /root/go
- GOROOT: /usr/lib/go
+ - name: install grpcio
+ command: "pip3 install --no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --no-cache-dir --prefer-binary --platform=armv6l --only-binary=:all: --target={{ pip_target.stdout }} https://www.piwheels.hostedpi.com/simple/grpcio/grpcio-1.24.1-cp37-cp37m-linux_armv6l.whl"
- - name: install bettercap
- copy:
- src: /root/go/bin/bettercap
- dest: /usr/bin/bettercap
+ - name: install tensorflow
+ command: "pip3 install --no-deps --extra-index-url=https://www.piwheels.hostedpi.com/simple/ --no-cache-dir --prefer-binary --platform=armv6l --only-binary=:all: --target={{ pip_target.stdout }} https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
+
+ - name: fetch bettercap release information
+ uri:
+ url: https://api.github.com/repos/bettercap/bettercap/releases/latest
+ return_content: yes
+ register: bettercap_release
+
+ - name: download and install bettercap
+ unarchive:
+ src: "{{ bettercap_release.content | from_json | json_query(bettercap.query) | first }}"
+ dest: /usr/bin
+ remote_src: yes
+ exclude:
+ - README.md
+ - LICENSE.md
mode: 0755
- name: clone bettercap caplets
@@ -151,10 +225,6 @@
path: /tmp/pwnagotchi
state: absent
- - name: install python modules
- pip:
- requirements: /root/pwnagotchi/scripts/requirements.txt
-
- name: create cpuusage script
copy:
dest: /usr/bin/cpuusage
@@ -249,11 +319,7 @@
insertafter: EOF
line: '{{ item }}'
with_items:
- - "dtoverlay=dwc2"
- - "dtparam=spi=on"
- - "dtoverlay=spi1-3cs"
- - "dtoverlay=pi3-disable-bt"
- - "dtparam=audio=off"
+ - "{{system.boot_options}}"
- name: change root partition
replace:
@@ -269,7 +335,7 @@
state: present
backup: no
regexp: '(.*)$'
- line: '\1 modules-load=dwc2,g_ether'
+ line: '\1 modules-load=dwc2,g_cdc'
- name: configure ssh
lineinfile:
@@ -281,7 +347,7 @@
- name: configure motd
copy:
dest: /etc/motd
- content: "(◕‿‿◕) {{pwn_hostname}} (pwnagotchi-{{pwn_version}})"
+ content: "(◕‿‿◕) {{pwnagotchi.hostname}} (pwnagotchi-{{pwnagotchi.version}})"
- name: clean apt cache
apt:
@@ -291,16 +357,21 @@
apt:
autoremove: yes
+ - name: enable services
+ systemd:
+ name: "{{services.enable}}"
+ state: started
+ enabled: yes
+
+ - name: disable unecessary services
+ systemd:
+ name: "{{services.disable}}"
+ state: stopped
+ enabled: no
+
- name: remove ssh keys
file:
state: absent
path: "{{item}}"
- with_items:
- - /etc/ssh/ssh_host_rsa_key
- - /etc/ssh/ssh_host_rsa_key.pub
- - /etc/ssh/ssh_host_dsa_key
- - /etc/ssh/ssh_host_dsa_key.pub
- - /etc/ssh/ssh_host/ecdsa_key
- - /etc/ssh/ssh_host/ecdsa_key.pub
- - /etc/ssh/ssh_host_ed25519_key
- - /etc/ssh/ssh_host_ed25519_key.pub
+ with_fileglob:
+ - "/etc/ssh/ssh_host*_key*"
diff --git a/docs/about.md b/docs/about.md
index 816b6c50..eacbd733 100644
--- a/docs/about.md
+++ b/docs/about.md
@@ -1,4 +1,4 @@
-## About the Project
+# About the Project
[Pwnagotchi](https://twitter.com/pwnagotchi) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment in order to maximize the WPA key material it captures (either passively, or by performing deauthentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/),
full and half WPA handshakes.
diff --git a/docs/configure.md b/docs/configure.md
index 98cd01ca..d2bb0f5a 100644
--- a/docs/configure.md
+++ b/docs/configure.md
@@ -1,11 +1,10 @@
-### Connecting to your Pwnagotchi
+# Connecting to your Pwnagotchi
-Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly, first, start with connecting the USB cable to the
-data port of the Raspberry Pi and the RPi to your computer. After a few seconds the board will boot and you will see a new Ethernet interface on your host computer.
+Once you wrote the image file on the SD card, there're a few steps you'll have to follow in order to configure your unit properly, first, start with connecting the USB cable to the data port of the Raspberry Pi and the RPi to your computer. After a few seconds the board will boot and you will see a new Ethernet interface on your host computer.
You'll need to configure it with a static IP address:
-- IP: `10.0.0.2`
+- IP: `10.0.0.1`
- Netmask: `255.255.255.0`
- Gateway: `10.0.0.1`
- DNS (if required): `8.8.8.8` (or whatever)
@@ -26,26 +25,34 @@ Moreover, it is recommended that you copy your SSH public key among the unit's a
ssh-copy-id -i ~/.ssh/id_rsa.pub pi@10.0.0.2
```
-### Configuration
+## Configuration
-You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by direclty editing the SD card contents from a computer) that will override
-the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values.
+You can now set a new name for your unit by [changing the hostname](https://geek-university.com/raspberry-pi/change-raspberry-pis-hostname/). Create the `/root/custom.yml` file (either via SSH or by direclty editing the SD card contents from a computer) that will override the [default configuration](https://github.com/evilsocket/pwnagotchi/blob/master/sdcard/rootfs/root/pwnagotchi/config.yml) with your custom values.
+
+## Language Selection
For instance, you can change `main.lang` to one of the supported languages:
-* **english** (default)
-* german
-* dutch
-* greek
-* macedonian
-* italian
-* french
+- **english** (default)
+- german
+- dutch
+- greek
+- macedonian
+- italian
+- french
+- russian
+- swedish
-The set the type of display you want to use via `ui.display.type` (if your display does not work after changing this setting, you might need to complete remove power from the Raspberry and make a clean boot).
+## Display Selection
+
+Set the type of display you want to use via `ui.display.type` (if your display does not work after changing this setting, you might need to completely remove power from the Raspberry and make a clean boot).
You can configure the refresh interval of the display via `ui.fps`, we advise to use a slow refresh to not shorten the lifetime of your display. The default value is 0, which will only refresh when changes are made to the screen.
-### Host Connection Share
+## Host Connection Share
-If you connect to the unit via `usb0` (thus using the data port), you might want to use the `scripts/linux_connection_share.sh` or
- `scripts/macos_connection_share.sh` script to bring the interface up on your end and share internet connectivity from another interface, so you can update the unit and generally download things from the internet on it.
\ No newline at end of file
+If you connect to the unit via `usb0` (thus using the data port), you might want to use the `scripts/linux_connection_share.sh`, `scripts/macos_connection_share.sh` or `scripts/win_connection_share.ps1` script to bring the interface up on your end and share internet connectivity from another interface, so you can update the unit and generally download things from the internet on it.
+
+## Troubleshooting
+
+If your network connection keeps flapping on your device connecting to your pwnagotchi, check if `usb0` (or equivalent) device is being controlled by NetworkManager. You can check this via `nmcli dev status`.
diff --git a/docs/dev.md b/docs/dev.md
index 5a4df640..75b7ea0f 100644
--- a/docs/dev.md
+++ b/docs/dev.md
@@ -1,4 +1,4 @@
-## Software
+# Software
- Raspbian + [nexmon patches](https://re4son-kernel.com/re4son-pi-kernel/) for monitor mode, or any Linux with a monitor mode enabled interface (if you tune config.yml).
@@ -21,6 +21,15 @@ usage: ./scripts/create_sibling.sh [OPTIONS]
-d # Only run dependencies checks
-h # Show this help
```
+
+#### Known Issues
+
+`GLib-ERROR **: 20:50:46.361: getauxval () failed: No such file or directory`
+
+- Affected DEB & Versions: QEMU <= 2.11
+- Fix: Upgrade QEMU to >= 3.1
+- Bug Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923289
+
## Adding a Language
If you want to add a language use the `language.sh` script. If you want to add for example the language **italian** you would type:
diff --git a/docs/faq.md b/docs/faq.md
index ed4a1f37..ff19087a 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -10,4 +10,4 @@ Because Python sucks and TF is huge.
## Why ...?
-Because!
\ No newline at end of file
+Because!
diff --git a/docs/hacks.md b/docs/hacks.md
new file mode 100644
index 00000000..184cff04
--- /dev/null
+++ b/docs/hacks.md
@@ -0,0 +1,38 @@
+# Unofficial Hacks
+---
+**IMPORTANT DISCLAIMER:** The information provided on this page is NOT officially supported by the Pwnagotchi development team. These are unofficial "hacks" that users have worked out while customizing their units and decided to document for anybody else who might want to do something similar.
+
+- **Please do NOT open issues if you cannot get something described in this document to work.**
+- It (almost) goes without saying, but obviously: **we are NOT responsible if you break your hardware by following any instructions documented here. Use this information at your own risk.**
+
+---
+If you test one of these hacks yourself and it still works, it's extra nice if you update the **Last Tested On** table and note any minor adjustments you may have had to make to the instructions to make it work with your particular Pwnagotchi setup. :heart:
+
+
+## Screens
+### Waveshare 3.5" SPI TFT screen
+
+Last tested on | Pwnagotchi version | Working? | Reference
+---------------|--------------------|----------|-----------|
+2019 October 3 | Unknown | :white_check_mark: | ([link](https://github.com/evilsocket/pwnagotchi/issues/124#issue-502346040))
+
+Some of this guide will work with other framebuffer-based displays.
+
+- First: SSH into your Pwnagotchi, and give it some internet!
+ - Don't forget to check your default gateway and `apt-get update`.
+- Follow the guide here: [www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)#Method_1._Driver_installation](https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)#Method_1._Driver_installation)
+ - At the step with `./LCD35-show`, add `lite` to the command prompt (e.g., `./LCD35-show lite`).
+- Reboot.
+- As root, make three symlinks:
+ - `cd ~`
+ - `ln -s pwnagotchi.png pwnagotchi_1.png`
+ - `ln -s pwnagotchi.png pwnagotchi_2.png`
+ - `ln -s pwnagotchi.png pwnagotchi_3.png`
+- `apt install fbi`
+- Change display type to `inky` in `config.yml`
+- Add `modules-load=dwc2,g_ether` to your kernel command line (`/boot/cmdline.txt`) or it will break!
+- Also must add `dtoverlay=dwc2` to the bottom of (`/boot/config.txt`)
+- Edit `/etc/rc.local` and add: `fbi -T 1 -a -noverbose -t 15 -cachemem 0 /root/pwnagotchi_1.png /root/pwnagotchi_2.png /root/pwnagotchi_3.png &`
+- Reboot.
+
+And you should be good!
diff --git a/docs/index.md b/docs/index.md
index 3c23b8c5..806c648d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -12,8 +12,9 @@
- [Project Slack](https://join.slack.com/t/pwnagotchi/shared_invite/enQtNzc4NzY3MDE2OTAzLTg5NmNmNDJiMDM3ZWFkMWUwN2Y5NDk0Y2JlZWZjODlhMmRhNDZiOGMwYjJhM2UzNzA3YjA5NjJmZGY5NGI5NmI)
- [Project Twitter](https://twitter.com/pwnagotchi)
+- [Project Subreddit](https://www.reddit.com/r/pwnagotchi/)
- [Project Website](https://pwnagotchi.ai/)
## License
-`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license.
\ No newline at end of file
+`pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It's released under the GPL3 license.
diff --git a/docs/install.md b/docs/install.md
index 4081e955..b1ee1d76 100644
--- a/docs/install.md
+++ b/docs/install.md
@@ -1,7 +1,9 @@
# Installation
-The project has been developed to run on a Raspberry Pi 0 W configured as an [USB Ethernet gadget](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) device in order to connect to it via USB.
-However, given the proper configuration tweaks, any GNU/Linux computer with a WiFi interface that supports monitor mode could be used.
+The project has been developed to run on a Raspberry Pi 0 W configured as an [USB Ethernet gadget](https://learn.adafruit.com/turning-your-raspberry-pi-zero-into-a-usb-gadget/ethernet-gadget) device in order to connect to it via USB. However, given the proper configuration tweaks, any GNU/Linux computer with a WiFi interface that supports monitor mode could be used.
+
+**An important note about the AI:** a network trained with a specific WiFi interface will only work with another interface if it supports
+the same exact WiFi channels of the first one. For instance, you can not use a neural network trained on a Raspberry Pi Zero W (that only supports 2.4Ghz channels) with a 5Ghz antenna, but you'll need to train one from scratch for those channels.
## Required Hardware
@@ -34,9 +36,7 @@ Color displays have a much slower refresh rate, in some cases it can take up to
The easiest way to create a new Pwnagotchi is downloading the latest stable image from [our release page](https://github.com/evilsocket/pwnagotchi/releases) and write it to your SD card. You will need to use an image writing tool to install the image you have downloaded on your SD card.
-[balenaEtcher](https://www.balena.io/etcher/) is a graphical SD card writing tool that works on Mac OS, Linux and Windows,
-and is the easiest option for most users. balenaEtcher also supports writing images directly from the zip file,
-without any unzipping required. To write your image with balenaEtcher:
+[balenaEtcher](https://www.balena.io/etcher/) is a graphical SD card writing tool that works on Mac OS, Linux and Windows, and is the easiest option for most users. balenaEtcher also supports writing images directly from the zip file, without any unzipping required. To write your image with balenaEtcher:
- Download the latest [Pwnagotchi .img file](https://github.com/evilsocket/pwnagotchi/releases).
- Download [balenaEtcher](https://www.balena.io/etcher/) and install it.
@@ -45,4 +45,4 @@ without any unzipping required. To write your image with balenaEtcher:
- Select the SD card you wish to write your image to.
- Review your selections and click 'Flash!' to begin writing data to the SD card.
-Your SD card is now ready for the first boot!
\ No newline at end of file
+Your SD card is now ready for the first boot!
diff --git a/docs/plugins.md b/docs/plugins.md
index 512c872a..0038df9f 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -1,8 +1,7 @@
# Plugins
Pwnagotchi has a simple plugins system that you can use to customize your unit and its behaviour. You can place your plugins anywhere
-as python files and then edit the `config.yml` file (`main.plugins` value) to point to their containing folder. Check the [plugins folder](https://github.com/evilsocket/pwnagotchi/tree/master/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/) for a list of default
-plugins and all the callbacks that you can define for your own customizations.
+as python files and then edit the `config.yml` file (`main.plugins` value) to point to their containing folder. Check the [plugins folder](https://github.com/evilsocket/pwnagotchi/tree/master/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/) for a list of default plugins and all the callbacks that you can define for your own customizations.
Here's as an example the GPS plugin:
diff --git a/docs/usage.md b/docs/usage.md
index 7d6c5d84..8883cb70 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -1,4 +1,6 @@
-### UI
+# Usage
+
+## User Interface
The UI is available either via display if installed, or via http://pwnagotchi.local:8080/ if you connect to the unit via `usb0` and set a static address on the network interface (change `pwnagotchi` with the hostname of your unit).
@@ -10,14 +12,107 @@ The UI is available either via display if installed, or via http://pwnagotchi.lo
* **PWND**: Number of handshakes captured in this session and number of unique networks we own at least one handshake of, from the beginning.
* **AUTO**: This indicates that the algorithm is running with AI disabled (or still loading), it disappears once the AI dependencies have been bootrapped and the neural network loaded.
-### BetterCAP's Web UI
+## Training the AI
-Moreover, given that the unit is running bettercap with API and Web UI, you'll be able to use the unit as a WiFi penetration testing portable station
-by accessing `http://pwnagotchi.local/`.
+At its core Pwnagotchi is a very simple creature: we could summarize its main algorithm as:
+
+```python
+# main loop
+while True:
+ # ask bettercap for all visible access points and their clients
+ aps = get_all_visible_access_points()
+ # loop each AP
+ for ap in aps:
+ # send an association frame in order to grab the PMKID
+ send_assoc(ap)
+ # loop each client station of the AP
+ for client in ap.clients:
+ # deauthenticate the client to get its half or full handshake
+ deauthenticate(client)
+
+ wait_for_loot()
+```
+
+Despite its simplicity, this logic is controlled by several parameters that regulate the wait times, the timeouts, on which channels to hop and so on.
+
+From `config.yml`:
+
+```yaml
+personality:
+ # advertise our presence
+ advertise: true
+ # perform a deauthentication attack to client stations in order to get full or half handshakes
+ deauth: true
+ # send association frames to APs in order to get the PMKID
+ associate: true
+ # list of channels to recon on, or empty for all channels
+ channels: []
+ # minimum WiFi signal strength in dBm
+ min_rssi: -200
+ # number of seconds for wifi.ap.ttl
+ ap_ttl: 120
+ # number of seconds for wifi.sta.ttl
+ sta_ttl: 300
+ # time in seconds to wait during channel recon
+ recon_time: 30
+ # number of inactive epochs after which recon_time gets multiplied by recon_inactive_multiplier
+ max_inactive_scale: 2
+ # if more than max_inactive_scale epochs are inactive, recon_time *= recon_inactive_multiplier
+ recon_inactive_multiplier: 2
+ # time in seconds to wait during channel hopping if activity has been performed
+ hop_recon_time: 10
+ # time in seconds to wait during channel hopping if no activity has been performed
+ min_recon_time: 5
+ # maximum amount of deauths/associations per BSSID per session
+ max_interactions: 3
+ # maximum amount of misses before considering the data stale and triggering a new recon
+ max_misses_for_recon: 5
+ # number of active epochs that triggers the excited state
+ excited_num_epochs: 10
+ # number of inactive epochs that triggers the bored state
+ bored_num_epochs: 15
+ # number of inactive epochs that triggers the sad state
+ sad_num_epochs: 25
+```
+
+There is no optimal set of parameters for every situation: when the unit is moving (during a walk for instance) smaller timeouts and RSSI thresholds might be preferred in order to quickly remove routers that are not in range anymore, while when stationary in high density areas (like an office) other parameters might be better. The role of the AI is to observe what's going on at the WiFi level, and adjust those parameters in order to maximize the cumulative reward of that loop / epoch.
+
+## Reward Function
+
+After each iteration of the main loop (an `epoch`), the reward, a score that represents how well the parameters performed, is computed as (an excerpt from `pwnagotchi/ai/reward.py`):
+
+```python
+# state contains the information of the last epoch
+# epoch_n is the number of the last epoch
+tot_epochs = epoch_n + 1e-20 # 1e-20 is added to avoid a division by 0
+tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + 1e-20
+tot_channels = wifi.NumChannels
+
+# ideally, for each interaction we would have an handshake
+h = state['num_handshakes'] / tot_interactions
+# small positive rewards the more active epochs we have
+a = .2 * (state['active_for_epochs'] / tot_epochs)
+# make sure we keep hopping on the widest channel spectrum
+c = .1 * (state['num_hops'] / tot_channels)
+# small negative reward if we don't see aps for a while
+b = -.3 * (state['blind_for_epochs'] / tot_epochs)
+# small negative reward if we interact with things that are not in range anymore
+m = -.3 * (state['missed_interactions'] / tot_interactions)
+# small negative reward for inactive epochs
+i = -.2 * (state['inactive_for_epochs'] / tot_epochs)
+
+reward = h + a + c + b + i + m
+```
+
+By maximizing this reward value, the AI learns over time to find the set of parameters that better perform with the current environmental conditions.
+
+## BetterCAP's Web UI
+
+Moreover, given that the unit is running bettercap with API and Web UI, you'll be able to use the unit as a WiFi penetration testing portable station by accessing `http://pwnagotchi.local/`.

-### Update your Pwnagotchi
+## Update your Pwnagotchi
You can use the `scripts/update_pwnagotchi.sh` script to update to the most recent version of pwnagotchi.
@@ -34,7 +129,7 @@ usage: ./update_pwnagitchi.sh [OPTIONS]
```
-### Backup your Pwnagotchi
+## Backup your Pwnagotchi
You can use the `scripts/backup.sh` script to backup the important files of your unit.
@@ -42,12 +137,10 @@ You can use the `scripts/backup.sh` script to backup the important files of your
usage: ./scripts/backup.sh HOSTNAME backup.zip
```
-### Random Info
-
-- **On a rpi0w, it'll take approximately 30 minutes to load the AI**.
-- `/var/log/pwnagotchi.log` is your friend.
-- if connected to a laptop via usb data port, with internet connectivity shared, magic things will happen.
-- checkout the `ui.video` section of the `config.yml` - if you don't want to use a display, you can connect to it with the browser and a cable.
-- If you get `[FAILED] Failed to start Remount Root and Kernel File Systems.` while booting pwnagotchi, make sure
-the `PARTUUID`s for `rootfs` and `boot` partitions are the same in `/etc/fstab`. Use `sudo blkid` to find those values when you are using `create_sibling.sh`.
+## Random Info
+* **On a rpi0w, it'll take approximately 30 minutes to load the AI**.
+* `/var/log/pwnagotchi.log` is your friend.
+* if connected to a laptop via usb data port, with internet connectivity shared, magic things will happen.
+* checkout the `ui.video` section of the `config.yml` - if you don't want to use a display, you can connect to it with the browser and a cable.
+* If you get `[FAILED] Failed to start Remount Root and Kernel File Systems.` while booting pwnagotchi, make sure the `PARTUUID`s for `rootfs` and `boot` partitions are the same in `/etc/fstab`. Use `sudo blkid` to find those values when you are using `create_sibling.sh`.
diff --git a/scripts/create_sibling.sh b/scripts/create_sibling.sh
index 55b784f4..02537cd5 100755
--- a/scripts/create_sibling.sh
+++ b/scripts/create_sibling.sh
@@ -5,7 +5,7 @@
set -eu
REQUIREMENTS=( wget gunzip git dd e2fsck resize2fs parted losetup qemu-system-x86_64 )
-DEBREQUIREMENTS=( wget gzip git parted qemu-system-x86 qemu-user-static bmap-tools )
+DEBREQUIREMENTS=( wget gzip git parted qemu-system-x86 qemu-user-static )
REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")"
TMP_DIR="${REPO_DIR}/tmp"
MNT_DIR="${TMP_DIR}/mnt"
@@ -93,15 +93,13 @@ function provide_raspbian() {
function setup_raspbian(){
# Detect the ability to create sparse files
- if [ "${OPT_SPARSE}" -eq 0 ];
- then
- which bmaptool >/dev/null 2>&1
- if [ $? -eq 0 ];
- then
+ if [ "${OPT_SPARSE}" -eq 0 ]; then
+ if [ which bmaptool -eq 0 ]; then
+ echo "[!] bmaptool not available, not creating a sparse image"
+
+ else
echo "[+] Defaulting to sparse image generation as bmaptool is available"
OPT_SPARSE=1
- else
- echo "[!] bmaptool not available, not creating a sparse image"
fi
fi
@@ -335,7 +333,13 @@ fi
setup_raspbian
provision_raspbian
-echo -e "[+] Congratz, it's a boy (⌐■_■)!"
+#Make a baby with a random gender, maybe do something fun with this later!
+gender[0]="boy"
+gender[1]="girl"
+
+rand=$[ $RANDOM % 2 ]
+
+echo -e "[+] Congratz, it's a ${gender[$rand]} (⌐■_■)!"
echo -e "[+] One more step: dd if=../${PWNI_OUTPUT} of= bs=4M status=progress"
if [ "${OPT_SPARSE}" -eq 1 ];
diff --git a/scripts/release.sh b/scripts/release.sh
index 8b8d8cd8..2f1343af 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# nothing to see here, just a utility i use to create new releases ^_^
-VERSION_FILE=$(dirname "${BASH_SOURCE[0]}")/../sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/version.py
+VERSION_FILE=$(dirname "${BASH_SOURCE[0]}")/../sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py
echo "version file is $VERSION_FILE"
CURRENT_VERSION=$(cat $VERSION_FILE | grep version | cut -d"'" -f2)
TO_UPDATE=(
diff --git a/scripts/update_pwnagotchi.sh b/scripts/update_pwnagotchi.sh
index 734ed4c2..9299f1a7 100755
--- a/scripts/update_pwnagotchi.sh
+++ b/scripts/update_pwnagotchi.sh
@@ -89,7 +89,7 @@ fi
if [ $BACKUPCONFIG -eq 1 ]; then
echo "[+] Creating backup of config.yml and hostname references"
- mv /root/pwnagotchi/config.yml /root/config.bak -f
+ mv /root/pwnagotchi/config.yml /root/config.yml.bak -f
mv /etc/hosts /root/hosts.bak -f
mv /etc/hostname /root/hostname.bak -f
mv /etc/motd /etc/motd.bak -f
diff --git a/scripts/win_connection_share.ps1 b/scripts/win_connection_share.ps1
new file mode 100644
index 00000000..c7c4ece8
--- /dev/null
+++ b/scripts/win_connection_share.ps1
@@ -0,0 +1,290 @@
+<#
+ .SYNOPSIS
+ A script that setups Internet Connection Sharing for Pwnagotchi.
+
+ .DESCRIPTION
+ A script that setups Internet Connection Sharing for Pwnagotchi.
+
+ Note: Internet Connection Sharing on Windows can be a bit unstable on between reboots.
+ You might need to run this script occasionally to disable and re-enable Internet Connection Sharing.
+
+ .PARAMETER EnableInternetConnectionSharing
+ Enable Internet Connection Sharing
+
+ .PARAMETER DisableInternetConnectionSharing
+ Disable Internet Connection Sharing
+
+ .PARAMETER SetPwnagotchiSubnet
+ Change the Internet Connection Sharing subnet to the Pwnagotchi subnet. The USB Gadget Interface IP will default to 10.0.0.1.
+
+ .PARAMETER ScopeAddress
+ Custom ScopeAddress (The IP Address of the USB Gadget Interface.)
+
+ .EXAMPLE
+ # Enable Internet Connection Sharing
+ PS C:\> .\win_connection_share -EnableInternetConnectionSharing
+
+ .EXAMPLE
+ # Disable Internet Connection Sharing
+ PS C:\> .\win_connection_share -DisableInternetConnectionSharing
+
+ .EXAMPLE
+ # Change the regkeys of Internet Connection Sharing to the Pwnagotchi Subnet
+ PS C:\> .\win_connection_share -SetPwnagotchiSubnet
+
+ .EXAMPLE
+ # Change the regkeys of Internet Connection Sharing to the Pwnagotchi Subnet with a custom ScopeAddress (The IP Address of the USB Gadget Interface.)
+ PS C:\> .\win_connection_share -SetPwnagotchiSubnet -ScopeAddress 10.0.0.10
+#>
+
+#Requires -Version 5
+#Requires -RunAsAdministrator
+[Cmdletbinding()]
+Param (
+ [switch]$EnableInternetConnectionSharing,
+ [switch]$DisableInternetConnectionSharing,
+ [switch]$SetPwnagotchiSubnet,
+ [ipaddress]$ScopeAddress = '10.0.0.1'
+)
+
+# Load helper functions
+Function Create-HNetObjects {
+ <#
+ .SYNOPSIS
+ A helper function that does the heavy lifiting with NetCfg.HNetShare
+
+ .DESCRIPTION
+ A helper function that does the heavy lifiting with NetCfg.HNetShare. This returns a PSObject containing the `INetSharingConfigurationForINetConnection` info of 2 Adapters.
+
+ .PARAMETER InternetAdaptor
+ The output of Get-NetAdaptor filtered down to the 'main' uplink interface.
+
+ .PARAMETER RNDISGadget
+ The output of Get-NetAdaptor filtered down to the 'USB Ethernet/RNDIS Gadget' interface.
+
+ .EXAMPLE
+ PS> $HNetObject = Create-HNetObjects
+ PS> $HNetObject
+ RNDISIntConfig InternetIntConfig
+ -------------- -----------------
+ System.__ComObject System.__ComObject
+ #>
+ [Cmdletbinding()]
+ Param (
+ $InternetAdaptor = $(Select-NetAdaptor -Message "Please select your main a ethernet adaptor with internet access that will be used for internet sharing."),
+ $RNDISGadget = $(Select-NetAdaptor -Message "Please select your 'USB Ethernet/RNDIS Gadget' adaptor")
+ )
+ Begin {
+ regsvr32.exe /s hnetcfg.dll
+ $HNetShare = New-Object -ComObject HNetCfg.HNetShare
+ }
+ Process {
+ if ($HNetShare.EnumEveryConnection -ne $null) {
+ $InternetInt = $HNetShare.EnumEveryConnection | Where-Object { $HNetShare.NetConnectionProps.Invoke($_).Name -eq ($InternetAdaptor).Name }
+ $InternetIntConfig = $HNetShare.INetSharingConfigurationForINetConnection.Invoke($InternetInt)
+ $RNDISInt = $HNetShare.EnumEveryConnection | Where-Object { $HNetShare.NetConnectionProps.Invoke($_).Name -eq ($RNDISGadget).Name }
+ $RNDISIntConfig = $HNetShare.INetSharingConfigurationForINetConnection.Invoke($RNDISInt)
+ }
+ }
+ End {
+ Return $(New-Object -TypeName PSObject -Property @{InternetIntConfig=$InternetIntConfig;RNDISIntConfig=$RNDISIntConfig;})
+ }
+}
+Function Enable-InternetConnectionSharing {
+ <#
+ .SYNOPSIS
+ Enables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
+
+ .DESCRIPTION
+ Enables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
+
+ .EXAMPLE
+ PS> Enable-InternetConnectionSharing
+
+ #>
+ [Cmdletbinding()]
+ $HNetObject = Create-HNetObjects
+ $HNetObject.InternetIntConfig.EnableSharing(0)
+ $HNetObject.RNDISIntConfig.EnableSharing(1)
+ Write-Output "[x] Enabled Internet Connection Sharing."
+}
+Function Disable-InternetConnectionSharing {
+ <#
+ .SYNOPSIS
+ Disables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
+
+ .DESCRIPTION
+ Disables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface.
+
+ .EXAMPLE
+ PS> Disable-InternetConnectionSharing
+
+ #>
+ [Cmdletbinding()]
+ $HNetObject = $(Create-HNetObjects)
+ $HNetObject.InternetIntConfig.DisableSharing()
+ $HNetObject.RNDISIntConfig.DisableSharing()
+ Write-Output "[x] Disabled Internet Connection Sharing."
+}
+Function Test-PwnagotchiSubnet {
+ <#
+ .SYNOPSIS
+ Tests the registry for the correct ScopeAddress.
+
+ .DESCRIPTION
+ Tests the registry for the correct ScopeAddress. By default windows uses a 192.168.137.x subnet for Internet Connection Sharing. This value can be changed
+ in the registry.
+
+ .EXAMPLE
+ PS> Test-PwnagotchiSubnet
+ [!] By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet.
+ #>
+ [Cmdletbinding()]
+ $RegKeys = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters -ErrorAction Stop
+ If ($RegKeys.ScopeAddress -notmatch '10.0.0.') {
+ Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet." -ErrorAction Stop
+ }
+ If ($RegKeys.ScopeAddressBackup -notmatch '10.0.0.') {
+ Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet." -ErrorAction Stop
+ }
+}
+Function Set-PwnagotchiSubnet {
+ <#
+ .SYNOPSIS
+ Set the registry for the correct ScopeAddress.
+
+ .DESCRIPTION
+ Set the registry for the correct ScopeAddress. By default windows uses a 192.168.137.x subnet for Internet Connection Sharing. This value can be changed
+ in the registry. By default it will be changed to 10.0.0.1
+
+ .PARAMETER ScopeAddress
+ The IP address the USB Gadget interface should use.
+
+ .EXAMPLE
+ Set-PwnagotchiSubnet
+
+ #>
+ [Cmdletbinding()]
+ Param (
+ $ScopeAddress = '10.0.0.1'
+ )
+ Try {
+ [void]([ipaddress]$ScopeAddress)
+ [void]([byte[]] $ScopeAddress.split('.'))
+ } Catch {
+ Write-Error "$ScopeAddress is not a valid IP."
+ }
+ Try {
+ Set-ItemProperty -Name ScopeAddress -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\" -Value $ScopeAddress -ErrorAction Stop
+ Set-ItemProperty -Name ScopeAddressBackup -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\" -Value $ScopeAddress -ErrorAction Stop
+ Write-Warning "The Internet Connection Sharing subnet has been updated. A reboot of windows is required !"
+ } Catch {
+ $PSCmdlet.ThrowTerminatingError($PSItem)
+ }
+
+}
+
+# Main Function
+Function Setup-PwnagotchiNetwork {
+ <#
+ .SYNOPSIS
+ Function to setup networking.
+
+ .DESCRIPTION
+ Function to setup networking. Main function calls helpers functions.
+
+ .PARAMETER EnableInternetConnectionSharing
+ Enable Internet Connection Sharing
+
+ .PARAMETER DisableInternetConnectionSharing
+ Disable Internet Connection Sharing
+
+ .PARAMETER SetPwnagotchiSubnet
+ Change the Internet Connection Sharing subnet to the Pwnagotchi. Defaults to 10.0.0.1.
+
+ .PARAMETER ScopeAddress
+ Custom ScopeAddress (the ICS ip address)
+
+ .EXAMPLE
+ PS> Setup-PwnagotchiNetwork -EnableInternetConnectionSharing
+
+ #>
+
+ Param (
+ [switch]$EnableInternetConnectionSharing,
+ [switch]$DisableInternetConnectionSharing,
+ [switch]$SetPwnagotchiSubnet,
+ $ScopeAddress = '10.0.0.1'
+ )
+ Begin {
+ Try {
+ Write-Debug "Begin"
+ $ErrorSplat=@{ErrorAction="stop"}
+ Write-Debug "Testing subnet"
+ Try {
+ Test-PwnagotchiSubnet @ErrorSplat
+ } Catch {
+ If ($SetPwnagotchiSubnet) {
+ Write-Debug "Setting subnet"
+ Set-PwnagotchiSubnet -ScopeAddress $ScopeAddress @ErrorSplat
+ } Else {
+ Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run this script with the -SetPwnagotchiSubnet to setup the network." -ErrorAction Stop
+ }
+ }
+ } Catch {
+ $PSCmdlet.ThrowTerminatingError($PSItem)
+ }
+ }
+ Process {
+ Write-Debug "Process"
+ Try {
+ If ($EnableInternetConnectionSharing) {
+ Write-Debug "Enable network Sharing"
+ Enable-InternetConnectionSharing @ErrorSplat
+ } ElseIf ($DisableInternetConnectionSharing) {
+ Write-Debug "Disable network Sharing"
+ Disable-InternetConnectionSharing @ErrorSplat
+ }
+ } Catch {
+ $PSCmdlet.ThrowTerminatingError($PSItem)
+ }
+ }
+ End {
+ Write-Debug "End"
+ Try {
+ # Nothing to return.
+ } Catch {
+ $PSCmdlet.ThrowTerminatingError($PSItem)
+ }
+ }
+}
+Function Select-NetAdaptor {
+ <#
+ .SYNOPSIS
+ A menu function to select the correct network adaptors.
+
+ .DESCRIPTION
+ A menu function to select the correct network adaptors.
+
+ .PARAMETER Message
+ Message that will be displayed during the question.
+
+ #>
+
+ Param (
+ $Message
+ )
+ $Adaptors = Get-NetAdapter | Where-Object {$_.MediaConnectionState -eq 'Connected'} | Sort-Object LinkSpeed -Descending
+ do {
+ Write-Host $Message
+ $index = 1
+ foreach ($Adaptor in $Adaptors) {
+ Write-Host "[$index] $($Adaptor.Name), $($Adaptor.InterfaceDescription)"
+ $index++
+ }
+ $Selection = Read-Host "Number"
+ } until ($Adaptors[$selection-1])
+ Return $Adaptors[$selection-1]
+}
+# Dynamically create params for Setup-PwnagotchiNetwork function based of param input of script.
+Setup-PwnagotchiNetwork @psBoundParameters
diff --git a/sdcard/rootfs/root/pwnagotchi/config.yml b/sdcard/rootfs/root/pwnagotchi/config.yml
index 28f27705..a93a7e89 100644
--- a/sdcard/rootfs/root/pwnagotchi/config.yml
+++ b/sdcard/rootfs/root/pwnagotchi/config.yml
@@ -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, ru, 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.yml
+ - /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 +%s).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
@@ -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
-
-
-
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/bettercap/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/bettercap/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/core/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/core/__init__.py
deleted file mode 100644
index eb718d31..00000000
--- a/sdcard/rootfs/root/pwnagotchi/scripts/core/__init__.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import glob
-import os
-import time
-import subprocess
-
-
-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)
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/main.py b/sdcard/rootfs/root/pwnagotchi/scripts/main.py
index c401c8e1..60469e82 100755
--- a/sdcard/rootfs/root/pwnagotchi/scripts/main.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/main.py
@@ -1,18 +1,13 @@
#!/usr/bin/python3
-import os
import argparse
import time
import logging
-import yaml
-
import pwnagotchi
import pwnagotchi.utils as utils
-import pwnagotchi.version as version
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
@@ -34,119 +29,75 @@ args = parser.parse_args()
config = utils.load_config(args)
utils.setup_logging(args, config)
-if args.do_clear:
- print("clearing the display ...")
- 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()
-
-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)
-logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, version.version))
-# for key, value in config['personality'].items():
-# logging.info(" %s: %s" % (key, value))
+logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent._identity, pwnagotchi.version))
for _, plugin in plugins.loaded.items():
- logging.info("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:
+if args.do_clear:
+ logging.info("clearing the display ...")
+ display.clear()
+
+elif args.do_manual:
logging.info("entering manual mode ...")
- log = SessionParser(config['main']['log'])
- 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))
+ 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 Agent.is_connected():
- plugins.on('internet_available', config, log)
+ plugins.on('internet_available', display, config, log)
- quit()
+else:
+ logging.info("entering auto mode ...")
-agent.start_ai()
-agent.setup_events()
-agent.set_starting()
-agent.start_monitor_mode()
-agent.start_event_polling()
+ agent.start()
-# print initial stats
-agent.next_epoch()
+ 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)
-agent.set_ready()
+ if not agent.is_stale() and agent.any_activity():
+ logging.info("%d access points on channel %d" % (len(aps), ch))
-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)
+ # 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)
- if not agent.is_stale() and agent.any_activity():
- logging.info("%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:
- logging.exception("main loop exception")
+ # 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")
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py
index dacb09c6..dd6c38ac 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/__init__.py
@@ -1,5 +1,7 @@
import subprocess
+version = '1.0.0plz3'
+
_name = None
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/agent.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/agent.py
index 578e3808..98f8aabe 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/agent.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/agent.py
@@ -7,10 +7,9 @@ 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
@@ -30,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
@@ -130,14 +129,24 @@ class Agent(Client, AsyncAdvertiser, AsyncTrainer):
wifi_running = self.is_module_running('wifi')
if wifi_running and restart:
logging.debug("restarting wifi module ...")
- self.restart('wifi.recon')
+ self.restart_module('wifi.recon')
self.run('wifi.clear')
elif not wifi_running:
logging.debug("starting wifi module ...")
- self.start('wifi.recon')
+ 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)
@@ -242,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):
@@ -262,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:
@@ -275,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())
@@ -379,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):
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/epoch.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/epoch.py
index 0ef7869c..11c2b6da 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/epoch.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ai/epoch.py
@@ -2,8 +2,8 @@ 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
@@ -174,22 +174,22 @@ class Epoch(object):
self._epoch_data_ready.set()
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,
- 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']))
+ "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
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/bettercap/client.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/bettercap.py
similarity index 100%
rename from sdcard/rootfs/root/pwnagotchi/scripts/bettercap/client.py
rename to sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/bettercap.py
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo
index 9a26d44b..fd5058bf 100644
Binary files a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo and b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.mo differ
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po
index 8cf48b45..fa9afdf2 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/de/LC_MESSAGES/voice.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:42+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"
@@ -188,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"
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.po
index fe6d817f..f113ce12 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.po
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/el/LC_MESSAGES/voice.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:44+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 \n"
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
@@ -189,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 ""
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.po
index 7110512c..1172f7fe 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.po
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/fr/LC_MESSAGES/voice.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:47+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"
@@ -190,3 +190,21 @@ 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 ""
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.mo b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.mo
index bcc0f8f5..53dfa24c 100644
Binary files a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.mo and b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.mo differ
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po
index d738a479..60cec0b0 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/it/LC_MESSAGES/voice.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:43+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"
@@ -187,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"
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.po
index 61d6a3fa..9dff7da0 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.po
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/mk/LC_MESSAGES/voice.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:35+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"
@@ -189,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 ""
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.po
index 72484e20..6f6f3e3a 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.po
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/nl/LC_MESSAGES/voice.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:43+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 \n"
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
@@ -188,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 ""
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo
new file mode 100644
index 00000000..08fae17c
Binary files /dev/null and b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo differ
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po
new file mode 100644
index 00000000..796bf17e
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/ru/LC_MESSAGES/voice.po
@@ -0,0 +1,205 @@
+# 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 <25989971+adolfaka@users.noreply.github.com>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-10-03 16:47+0200\n"
+"PO-Revision-Date: 2019-10-05 18:50+0300\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.4\n"
+"Last-Translator: Elliot Manson\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
+"Language: ru_RU\n"
+
+msgid "ZzzzZZzzzzZzzz"
+msgstr "ZzzzZZzzzzZzzz"
+
+msgid "Hi, I'm Pwnagotchi! Starting ..."
+msgstr "Привет, я Pwnagotchi! Поехали …"
+
+msgid "New day, new hunt, new pwns!"
+msgstr "Новый день, новая охота, новые взломы!"
+
+msgid "Hack the Planet!"
+msgstr "Взломаем всю планету!"
+
+msgid "AI ready."
+msgstr "Искусственный интеллект готов."
+
+msgid "The neural network is ready."
+msgstr "Нейронная сеть готова."
+
+#, python-brace-format
+msgid "Hey, channel {channel} is free! Your AP will say thanks."
+msgstr "Эй, канал {channel} свободен! Ваша точка доступа скажет спасибо."
+
+msgid "I'm bored ..."
+msgstr "Мне скучно …"
+
+msgid "Let's go for a walk!"
+msgstr "Пойдем прогуляемся!"
+
+msgid "This is the best day of my life!"
+msgstr "Это лучший день в моей жизни!"
+
+msgid "Shitty day :/"
+msgstr "Дерьмовый день :/"
+
+msgid "I'm extremely bored ..."
+msgstr "Мне очень скучно …"
+
+msgid "I'm very sad ..."
+msgstr "Мне очень грустно …"
+
+msgid "I'm sad"
+msgstr "Мне грустно"
+
+msgid "I'm living the life!"
+msgstr "Я живу своей жизнью!"
+
+msgid "I pwn therefore I am."
+msgstr "Я взламываю, поэтому я существую."
+
+msgid "So many networks!!!"
+msgstr "Так, много сетей!!!"
+
+msgid "I'm having so much fun!"
+msgstr "Мне так весело!"
+
+msgid "My crime is that of curiosity ..."
+msgstr "Моё преступление - это любопытство …"
+
+#, python-brace-format
+msgid "Hello {name}! Nice to meet you. {name}"
+msgstr "Привет, {name}! Приятно познакомиться. {name}"
+
+#, python-brace-format
+msgid "Unit {name} is nearby! {name}"
+msgstr "Цель {name} близко! {name}"
+
+#, python-brace-format
+msgid "Uhm ... goodbye {name}"
+msgstr "Хм … до свидания {name}"
+
+#, python-brace-format
+msgid "{name} is gone ..."
+msgstr "{name} исчезла …"
+
+#, python-brace-format
+msgid "Whoops ... {name} is gone."
+msgstr "Упс … {name} исчезла."
+
+#, python-brace-format
+msgid "{name} missed!"
+msgstr "{name} упустил!"
+
+msgid "Missed!"
+msgstr "Промахнулся!"
+
+msgid "Nobody wants to play with me ..."
+msgstr "Никто не хочет играть со мной …"
+
+msgid "I feel so alone ..."
+msgstr "Мне так одиноко …"
+
+msgid "Where's everybody?!"
+msgstr "Где все?!"
+
+#, python-brace-format
+msgid "Napping for {secs}s ..."
+msgstr "Дремлет {secs}с …"
+
+msgid "Zzzzz"
+msgstr "Zzzzz"
+
+#, python-brace-format
+msgid "ZzzZzzz ({secs}s)"
+msgstr "ZzzZzzz ({secs}c)"
+
+#, python-brace-format
+msgid "Waiting for {secs}s ..."
+msgstr "Ждем {secs}c …"
+
+#, python-brace-format
+msgid "Looking around ({secs}s)"
+msgstr "Оглядываюсь вокруг ({secs}с)"
+
+#, python-brace-format
+msgid "Hey {what} let's be friends!"
+msgstr "Эй, {what} давай дружить!"
+
+#, python-brace-format
+msgid "Associating to {what}"
+msgstr "Связываюсь с {what}"
+
+#, python-brace-format
+msgid "Yo {what}!"
+msgstr "Йоy {what}!"
+
+#, python-brace-format
+msgid "Just decided that {mac} needs no WiFi!"
+msgstr "Просто решил, что {mac} не нужен WiFi! Кхе-кхе)"
+
+#, python-brace-format
+msgid "Deauthenticating {mac}"
+msgstr "Деаутентификация {mac}"
+
+#, python-brace-format
+msgid "Kickbanning {mac}!"
+msgstr "Кикаю {mac}!"
+
+#, python-brace-format
+msgid "Cool, we got {num} new handshake{plural}!"
+msgstr "Круто, мы получили {num} новое рукопожатие!"
+
+msgid "Ops, something went wrong ... Rebooting ..."
+msgstr "Ой, что-то пошло не так … Перезагружаюсь …"
+
+#, python-brace-format
+msgid "Kicked {num} stations\n"
+msgstr "Кикнул {num} станцию\n"
+
+#, python-brace-format
+msgid "Made {num} new friends\n"
+msgstr "Заимел {num} новых друзей\n"
+
+#, python-brace-format
+msgid "Got {num} handshakes\n"
+msgstr "Получил {num} рукопожатие\n"
+
+msgid "Met 1 peer"
+msgstr "Встретился один знакомый"
+
+#, python-brace-format
+msgid "Met {num} peers"
+msgstr "Встретились {num} приятелей"
+
+#, 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 ""
+"Я взламывал {duration} и кикнул {deauthed} клиентов! Я также встретил "
+"{associated} новых друзей и съел {handshakes} рукопожатий! #pwnagotchi "
+"#pwnlog #pwnlife #hacktheplanet #skynet"
+
+msgid "hours"
+msgstr "часов"
+
+msgid "hour"
+msgstr "час"
+
+msgid "minutes"
+msgstr "минут"
+
+msgid "minute"
+msgstr "минуту"
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.mo b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.mo
new file mode 100644
index 00000000..7c0eb0f9
Binary files /dev/null and b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.mo differ
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.po b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.po
new file mode 100644
index 00000000..e8400772
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/se/LC_MESSAGES/voice.po
@@ -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 , 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 \n"
+"Language-Team: LANGUAGE \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"
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot
index fbeba17d..b6489ba4 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/locale/voice.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-03 16:47+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 \n"
"Language-Team: LANGUAGE \n"
@@ -186,3 +186,21 @@ msgid ""
"{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 ""
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/log.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/log.py
index 8648521d..416ae4f8 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/log.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/log.py
@@ -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
@@ -125,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 = ''
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/advertise.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/advertise.py
index 90374c69..ff797a35 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/advertise.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/advertise.py
@@ -86,7 +86,9 @@ class Advertiser(object):
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:
logging.exception("error")
time.sleep(self._period)
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/utils.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/utils.py
index be7bb0f4..3b63d092 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/utils.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/mesh/utils.py
@@ -2,7 +2,6 @@ import _thread
import logging
import pwnagotchi
-import pwnagotchi.version as version
import pwnagotchi.plugins as plugins
from pwnagotchi.mesh import get_identity
@@ -24,7 +23,7 @@ class AsyncAdvertiser(object):
self._advertiser = Advertiser(
self._config['main']['iface'],
pwnagotchi.name(),
- version.version,
+ pwnagotchi.version,
self._identity,
period=0.3,
data=self._config['personality'])
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/__init__.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/__init__.py
index bcb89f8e..38d29e73 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/__init__.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/__init__.py
@@ -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')
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py
new file mode 100644
index 00000000..731a40ff
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-backup.py
@@ -0,0 +1,63 @@
+__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
+ logging.info("AUTO-BACKUP: Successfuly loaded.")
+
+
+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:
+ logging.info("AUTO-BACKUP: Backing up ...")
+ display.set('status', 'Backing up ...')
+ display.update()
+
+ for cmd in OPTIONS['commands']:
+ logging.info(f"AUTO-BACKUP: Running {cmd.format(files=files_to_backup)}")
+ process = subprocess.Popen(cmd.format(files=files_to_backup), shell=True, stdin=None,
+ stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash")
+ process.wait()
+ if process.returncode > 0:
+ raise OSError(f"Command failed (rc: {process.returncode})")
+
+ 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()
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-update.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-update.py
new file mode 100644
index 00000000..4a1f1352
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/auto-update.py
@@ -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()
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/example.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/example.py
index 9c1c8c42..c18177c2 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/example.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/example.py
@@ -3,7 +3,6 @@ __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
@@ -12,13 +11,16 @@ from pwnagotchi.ui.view import BLACK
import pwnagotchi.ui.fonts as fonts
+# Will be set with the options in config.yml config['main']['plugins'][__name__]
+OPTIONS = dict()
+
# called when the plugin is loaded
def on_loaded():
logging.warning("WARNING: plugin %s should be disabled!" % __name__)
# called in manual mode when there's internet connectivity
-def on_internet_available(config, log):
+def on_internet_available(ui, config, log):
pass
@@ -82,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
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/gps.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/gps.py
index 7c57b24c..f7aa56e6 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/gps.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/gps.py
@@ -3,7 +3,6 @@ __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 logging
import json
@@ -15,14 +14,14 @@ running = False
def on_loaded():
- logging.info("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):
- logging.info("enabling GPS bettercap's module for %s" % device)
+ logging.info("enabling gps bettercap's module for %s" % device)
try:
agent.run('gps off')
except:
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/memtemp.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/memtemp.py
index 2b1fc3da..4ef823b0 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/memtemp.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/memtemp.py
@@ -7,7 +7,6 @@ __version__ = '1.0.0'
__name__ = 'memtemp'
__license__ = 'GPL3'
__description__ = 'A plugin that will add a memory and temperature indicator'
-__enabled__ = False
import struct
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/onlinehashcrack.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/onlinehashcrack.py
new file mode 100644
index 00000000..249ea19a
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/onlinehashcrack.py
@@ -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}")
+
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/twitter.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/twitter.py
index 7aa4364b..560903a0 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/twitter.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/twitter.py
@@ -3,21 +3,19 @@ __version__ = '1.0.0'
__name__ = 'twitter'
__license__ = 'GPL3'
__description__ = 'This plugin creates tweets about the recent activity of pwnagotchi'
-__enabled__ = True
import logging
from pwnagotchi.voice import Voice
-UI = None
-
+OPTIONS = dict()
def on_loaded():
- logging.info("Twitter plugin loaded.")
+ logging.info("twitter plugin loaded.")
# called in manual mode when there's internet connectivity
-def on_internet_available(config, log):
- if config['twitter']['enabled'] and log.is_new() and log.handshakes > 0 and UI:
+def on_internet_available(ui, config, log):
+ if log.is_new() and log.handshakes > 0:
try:
import tweepy
except ImportError:
@@ -28,15 +26,15 @@ def on_internet_available(config, log):
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)
+ 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(config['twitter']['consumer_key'], config['twitter']['consumer_secret'])
- auth.set_access_token(config['twitter']['access_token_key'], config['twitter']['access_token_secret'])
+ 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)
@@ -46,9 +44,3 @@ def on_internet_available(config, log):
logging.info("tweeted: %s" % tweet)
except Exception as e:
logging.exception("error while tweeting")
-
-
-def on_ui_setup(ui):
- # need that object
- global UI
- UI = ui
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/ups_lite.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/ups_lite.py
index 7ddcecdd..d9a37d28 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/ups_lite.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/ups_lite.py
@@ -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
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/wpa-sec.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/wpa-sec.py
new file mode 100644
index 00000000..68e41717
--- /dev/null
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/plugins/default/wpa-sec.py
@@ -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}")
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py
index c8b301a6..76e9a24d 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/display.py
@@ -102,12 +102,15 @@ 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():
logging.info("initializing inky display")
@@ -124,7 +127,7 @@ class Display(View):
self._display.clear()
self._render_cb = self._papirus_render
- elif self._is_waveshare1():
+ elif self._is_waveshare_v1():
logging.info("initializing waveshare v1 display")
from pwnagotchi.ui.waveshare.v1.epd2in13 import EPD
self._display = EPD()
@@ -133,7 +136,7 @@ class Display(View):
self._display.init(self._display.lut_partial_update)
self._render_cb = self._waveshare_render
- elif self._is_waveshare2():
+ elif self._is_waveshare_v2():
logging.info("initializing waveshare v2 display")
from pwnagotchi.ui.waveshare.v2.waveshare import EPD
self._display = EPD()
@@ -149,6 +152,18 @@ class Display(View):
self.on_render(self._on_view_rendered)
+ 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':
display_colors = 3
@@ -175,7 +190,10 @@ class Display(View):
])
self._display.set_image(img_buffer)
- self._display.show()
+ try:
+ self._display.show()
+ except:
+ print("")
def _papirus_render(self):
self._display.display(self._canvas)
@@ -183,9 +201,9 @@ class Display(View):
def _waveshare_render(self):
buf = self._display.getbuffer(self._canvas)
- if self._is_waveshare1():
+ if self._is_waveshare_v1():
self._display.display(buf)
- elif self._is_waveshare2():
+ elif self._is_waveshare_v2():
self._display.displayPartial(buf)
def image(self):
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py
index f86b88f0..78e75b6c 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/ui/view.py
@@ -4,7 +4,7 @@ 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
@@ -86,8 +86,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),
@@ -166,7 +167,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):
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/utils.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/utils.py
index 8761a59c..d1b2ba0d 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/utils.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/utils.py
@@ -1,6 +1,10 @@
-import yaml
-import os
+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
@@ -40,3 +44,55 @@ def setup_logging(args, config):
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)
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/version.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/version.py
deleted file mode 100644
index 17f3421b..00000000
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/version.py
+++ /dev/null
@@ -1 +0,0 @@
-version = '1.0.0plz2'
diff --git a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py
index 536bc179..9268b113 100644
--- a/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py
+++ b/sdcard/rootfs/root/pwnagotchi/scripts/pwnagotchi/voice.py
@@ -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