Compare commits
518 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ff91f9b2f8 | ||
|
07b70dd0ee | ||
|
c12b319287 | ||
|
712d0142a1 | ||
|
9ba55b7f77 | ||
|
abc16ce973 | ||
|
1318275b36 | ||
|
297d9cd3b9 | ||
|
f164b8bb26 | ||
|
5c3b21f537 | ||
|
6325428218 | ||
|
7d35f5cdc0 | ||
|
3fd3ac3c01 | ||
|
c1d3528ff7 | ||
|
37fc65f834 | ||
|
80095edf4b | ||
|
7e3e74635a | ||
|
6b3d9042fd | ||
|
4441ae852c | ||
|
edc0551e0a | ||
|
b6cf510f8e | ||
|
7fb6be861a | ||
|
f795598950 | ||
|
b0efd52961 | ||
|
38dfccb7c2 | ||
|
f912883f6e | ||
|
661f26dedf | ||
|
d6c7a73f39 | ||
|
10f274dab7 | ||
|
decbeaccb1 | ||
|
34c2c8a06e | ||
|
88a15528d6 | ||
|
0fd09878db | ||
|
c472e60615 | ||
|
0704541dd1 | ||
|
ea061d473b | ||
|
6430a40847 | ||
|
ba13b12593 | ||
|
819be761ed | ||
|
f701390d5a | ||
|
2ddf040fac | ||
|
3bd9cd4f18 | ||
|
5c6d8dc807 | ||
|
c535ffd40e | ||
|
fb7217b0fa | ||
|
7da3cc5565 | ||
|
e72e2292a8 | ||
|
0f8643258e | ||
|
1a0083eb38 | ||
|
72878454f9 | ||
|
1c4df7a1c4 | ||
|
c124a97514 | ||
|
b886b4e673 | ||
|
6d0e295276 | ||
|
1aea0b95b1 | ||
|
fddee8708e | ||
|
ef4fbd96cc | ||
|
552df65422 | ||
|
2db8f143eb | ||
|
6111ee9d9d | ||
|
713a14c504 | ||
|
2708fd032c | ||
|
f7cf4b3947 | ||
|
1d312a727b | ||
|
dd3dbbe400 | ||
|
cfa034b555 | ||
|
a0cd0d4936 | ||
|
3e3fff298c | ||
|
0b1c51dcc7 | ||
|
8dd9a85615 | ||
|
929eac7bba | ||
|
0f7870f770 | ||
|
37342c0685 | ||
|
71514a97fa | ||
|
03488819af | ||
|
633b726bcd | ||
|
05b235c38b | ||
|
840054f549 | ||
|
a3cf49244e | ||
|
e92751164f | ||
|
c2f9860bc5 | ||
|
f616871068 | ||
|
c726779f6e | ||
|
a2e29d64d2 | ||
|
bc84f22f7a | ||
|
40d8d994d1 | ||
|
7ca5eee247 | ||
|
56c291d302 | ||
|
1013e7dc19 | ||
|
ff4f5c672a | ||
|
d9d268ea81 | ||
|
de62214970 | ||
|
52cc413152 | ||
|
716d5cd312 | ||
|
e436dc8b8e | ||
|
81db495f33 | ||
|
7f8380c38a | ||
|
8c2b4e2075 | ||
|
2b17e5322b | ||
|
1be17b1f99 | ||
|
2dee3987d4 | ||
|
eb76cc7023 | ||
|
35ea36ef33 | ||
|
44e1e79245 | ||
|
6038f555fa | ||
|
0b5a63a3d8 | ||
|
430172e3dd | ||
|
fa87e03222 | ||
|
d1411ffa96 | ||
|
67b4747afa | ||
|
311931c81d | ||
|
1f7bc60de6 | ||
|
57034d9fc6 | ||
|
74fbf4da32 | ||
|
7ec20caf23 | ||
|
568c5b020d | ||
|
3965bdb554 | ||
|
585b208e9e | ||
|
e53bdc46a4 | ||
|
6805df858e | ||
|
8a07e822e6 | ||
|
5f7dd56ead | ||
|
a808fd33c7 | ||
|
cedcc17391 | ||
|
6478b3827d | ||
|
68065d548a | ||
|
463f4b50e4 | ||
|
793cde7147 | ||
|
518b9e665d | ||
|
91ea7bdb9b | ||
|
3ce88f1d80 | ||
|
6d45d01baf | ||
|
1f2dd73976 | ||
|
5d8d86204a | ||
|
7017e39c6d | ||
|
1360c734ff | ||
|
7c90050b17 | ||
|
9ca8aacdf6 | ||
|
d39c849daf | ||
|
58bbae89c2 | ||
|
0dedd0974f | ||
|
5bac678771 | ||
|
3b9aacdd16 | ||
|
305f837486 | ||
|
54ffbbcb0b | ||
|
60167fb8fe | ||
|
9a1565813c | ||
|
03c014f414 | ||
|
d10bf6bf1d | ||
|
9a22321799 | ||
|
76b71f5c3f | ||
|
4aa05bb834 | ||
|
71c4458858 | ||
|
8260b41bab | ||
|
86530d4b97 | ||
|
c7d9a757f6 | ||
|
b6a0ae9d3a | ||
|
34f52b0d3a | ||
|
c68cefe80d | ||
|
052c99b858 | ||
|
ccea7cbeef | ||
|
a16a5f7bcb | ||
|
a5df77d737 | ||
|
489bce0c01 | ||
|
da4319f81b | ||
|
0e1a1f4c79 | ||
|
b3bdb34e3f | ||
|
c791c86bee | ||
|
61e5872229 | ||
|
23616095ba | ||
|
3c8e7fbea4 | ||
|
4a5d2d36cc | ||
|
52d432e5b6 | ||
|
93bdf2e3a1 | ||
|
fe97315b0f | ||
|
7e80a7b9ca | ||
|
64385f43ed | ||
|
6a4d7a895e | ||
|
5606ad7281 | ||
|
23f09bc4b6 | ||
|
238b90d988 | ||
|
138316d55a | ||
|
67bbcfef9b | ||
|
1a0e0c46d2 | ||
|
4cd0f46ad7 | ||
|
155ea54d08 | ||
|
461e53ed79 | ||
|
6d40388002 | ||
|
37b25a142f | ||
|
665ad938b4 | ||
|
301a3d99cf | ||
|
c4e0acad17 | ||
|
9339ecb2fd | ||
|
a28c9a1176 | ||
|
c5d6f6d362 | ||
|
ff843f0367 | ||
|
717cb02743 | ||
|
8be643b2e0 | ||
|
814392daa5 | ||
|
4cc1c2ac1f | ||
|
fae6a0942b | ||
|
53ab63cf8a | ||
|
0764304be9 | ||
|
cdc0e0fa3e | ||
|
5ccd65e46e | ||
|
afc3636939 | ||
|
f154b97ab9 | ||
|
66acecb387 | ||
|
a31d0a5e19 | ||
|
026b9fc513 | ||
|
1cdc1641fc | ||
|
71de5925ee | ||
|
4733e90e77 | ||
|
7cf0a2ef4b | ||
|
97e03843bd | ||
|
8b078383c2 | ||
|
3e5bece3cf | ||
|
7645be6f3e | ||
|
779da95f78 | ||
|
51e13aa1ad | ||
|
e489678cf5 | ||
|
52015014b4 | ||
|
f691f737ab | ||
|
2617a6edea | ||
|
6001b7630b | ||
|
78fba1f74b | ||
|
6075296884 | ||
|
9457622713 | ||
|
7ef1c1f2f0 | ||
|
8e0488e16f | ||
|
5934ac4a55 | ||
|
15bae093fb | ||
|
0c76cd7ea7 | ||
|
5834b27ed8 | ||
|
a8ed9bcc1c | ||
|
0e88c3aa6a | ||
|
b1d61d95e6 | ||
|
215af0fc88 | ||
|
c09b72ff7e | ||
|
2f1b35b3fa | ||
|
d435ef2ba9 | ||
|
4164e7c067 | ||
|
bb7737762c | ||
|
c300e73726 | ||
|
0587c4b09a | ||
|
63dc672b11 | ||
|
0dac137df0 | ||
|
3db9ccb47e | ||
|
f375e4905f | ||
|
8d17cf0bd2 | ||
|
d981b26842 | ||
|
a7164ea742 | ||
|
cae2a18016 | ||
|
9d63eba232 | ||
|
f141e15ba3 | ||
|
e68165ce06 | ||
|
3758806919 | ||
|
1f91c6f09e | ||
|
3e8b6eafbd | ||
|
44138ba463 | ||
|
4b71fea404 | ||
|
6babad0d02 | ||
|
e8513240ea | ||
|
00101ccd07 | ||
|
a0bc911c0e | ||
|
6d71bcd965 | ||
|
91447a2a31 | ||
|
e06480e474 | ||
|
819146f83a | ||
|
cdd4c13336 | ||
|
704d7ceaa1 | ||
|
a4daf4af61 | ||
|
eddcf32b62 | ||
|
6117235c52 | ||
|
e0a66f5c99 | ||
|
81061cea24 | ||
|
93bb633010 | ||
|
dbb64e0fab | ||
|
fa8751017d | ||
|
9d56c97aa5 | ||
|
10f7161240 | ||
|
9b02548176 | ||
|
f5f47c4f88 | ||
|
1523dfc1ef | ||
|
a02960b56d | ||
|
b6f59f99d4 | ||
|
09a00adab9 | ||
|
7fa30c2868 | ||
|
774d9c693c | ||
|
87b6cf7d40 | ||
|
88928eec82 | ||
|
60f7849838 | ||
|
3cf041617c | ||
|
ee6c06f306 | ||
|
2e22a17610 | ||
|
1615fc8817 | ||
|
714cb00610 | ||
|
a0a790635a | ||
|
9d17be959d | ||
|
4b651cd17b | ||
|
2f70512076 | ||
|
b79c59c639 | ||
|
56f7b67699 | ||
|
1a8472268e | ||
|
4fb7205281 | ||
|
f8ffab426b | ||
|
b4daf19401 | ||
|
e04e053cee | ||
|
4f153e3899 | ||
|
04720ecc42 | ||
|
a12e2aafa5 | ||
|
1721f67ec3 | ||
|
7693e42aa1 | ||
|
2ae48a2ef2 | ||
|
663bca41cd | ||
|
ede01e50cd | ||
|
19973574e7 | ||
|
a019b4c778 | ||
|
526c5bed87 | ||
|
4d6136633a | ||
|
6d90b75d10 | ||
|
03695be807 | ||
|
6a97476732 | ||
|
b5e620684b | ||
|
95557ab37d | ||
|
988d093e36 | ||
|
f563d71477 | ||
|
7b219fd139 | ||
|
42ed698583 | ||
|
6df7bcd885 | ||
|
1c299832ae | ||
|
11476433ca | ||
|
6e57e131b3 | ||
|
548b42ef20 | ||
|
99614c8cd4 | ||
|
e19ea999e2 | ||
|
2207a1eacf | ||
|
9509dd0aa5 | ||
|
608904daf8 | ||
|
f973997cdb | ||
|
9b594f7fb2 | ||
|
b8eed4f52a | ||
|
ad510429fe | ||
|
f5a94fde96 | ||
|
855bda9104 | ||
|
e72fd08fb4 | ||
|
e44ebac43f | ||
|
b5ddb716e2 | ||
|
82e7e09fa1 | ||
|
8b40e94ca8 | ||
|
cc5c46906f | ||
|
7cb52ba33a | ||
|
07f8e7bd4a | ||
|
48dc751d13 | ||
|
3c154ffe0c | ||
|
167f559d73 | ||
|
19775b7d27 | ||
|
48e3a372cc | ||
|
d2c44797e5 | ||
|
a03443986b | ||
|
a7ea499fac | ||
|
722a91655a | ||
|
93e06d7f59 | ||
|
7de5121033 | ||
|
83f741bbb0 | ||
|
a779fb9b0b | ||
|
c4a007e72a | ||
|
1a71615fa8 | ||
|
7a9f84f495 | ||
|
6e3f5a1181 | ||
|
d045ed5afa | ||
|
0ee0aaff37 | ||
|
0fb81a11c4 | ||
|
cfc0ad1b48 | ||
|
3351c251ef | ||
|
d9d399429c | ||
|
b1ad247e11 | ||
|
b5a148f287 | ||
|
7138f6469b | ||
|
785d678e30 | ||
|
9c8784e533 | ||
|
fd288b4acd | ||
|
b704614254 | ||
|
2dc36651df | ||
|
d8d6d52eda | ||
|
43c5ab7ecf | ||
|
de62424dbc | ||
|
8c51936c13 | ||
|
87e46610f9 | ||
|
a96dead519 | ||
|
8ed2950eb5 | ||
|
6f8133b2b8 | ||
|
16afa87112 | ||
|
ed22343877 | ||
|
c70f2c30e9 | ||
|
5111490c70 | ||
|
59ae35372e | ||
|
5f593a4231 | ||
|
56cc872daa | ||
|
6e1490da78 | ||
|
69597103b5 | ||
|
96ca5dd8e3 | ||
|
3efa3a935a | ||
|
39ccd141eb | ||
|
1a30b52a90 | ||
|
8965ad9272 | ||
|
5dae0ce982 | ||
|
92266a783a | ||
|
9e656d4ea6 | ||
|
1d255b577d | ||
|
1ff14c05a9 | ||
|
ab63ecccd7 | ||
|
a7e37115d9 | ||
|
b1d8aa3ba1 | ||
|
6e26463278 | ||
|
7261073073 | ||
|
a02c1d6d92 | ||
|
40caf3f51a | ||
|
21f1273bd8 | ||
|
a8c07ba997 | ||
|
6bd09c7f43 | ||
|
1830a19b37 | ||
|
9dcc647656 | ||
|
8fcfd4cafd | ||
|
369d7a65a8 | ||
|
d7ad8ee0d7 | ||
|
a5f9b9b2ee | ||
|
b266671864 | ||
|
c47b8f2d11 | ||
|
8129fb7dd2 | ||
|
c16cabc852 | ||
|
9a7de86057 | ||
|
440f2a470a | ||
|
81a89d43e0 | ||
|
91b409053b | ||
|
df01a03a4b | ||
|
e2be21004d | ||
|
0f3d9db01d | ||
|
19abc17816 | ||
|
4b9ebc2512 | ||
|
41a3fad43e | ||
|
f4b886cf7b | ||
|
0eb8e1829e | ||
|
30b1874a0a | ||
|
b903f636d2 | ||
|
52b40f049e | ||
|
3df35ef03b | ||
|
e87bcc4744 | ||
|
dfd534ac41 | ||
|
0b2c156d29 | ||
|
14064c3b5b | ||
|
313fd66634 | ||
|
c939af4248 | ||
|
e934181606 | ||
|
2505cbf14c | ||
|
b2c6de72cd | ||
|
307b3890f1 | ||
|
b9a909de2b | ||
|
b180f16aa6 | ||
|
2d517e3de5 | ||
|
a1746da7f1 | ||
|
1a1a70d6e8 | ||
|
229e2671e8 | ||
|
92c1b6b005 | ||
|
a2ac679499 | ||
|
d7e1c59709 | ||
|
a5ed09cd08 | ||
|
8c83f8129c | ||
|
eddfdb3ebc | ||
|
7ca1168fed | ||
|
d41e5c1152 | ||
|
25eee18e7b | ||
|
38144a7abb | ||
|
1f17d3cbbe | ||
|
1da59b50b4 | ||
|
1130c72098 | ||
|
6b99deb7bd | ||
|
c3ed3509e9 | ||
|
e3a2e8c811 | ||
|
89046bf0c5 | ||
|
4cc61322de | ||
|
94521f2174 | ||
|
b50acd364c | ||
|
9bc7fcccb3 | ||
|
bd61196c3c | ||
|
186042aa20 | ||
|
78bf801273 | ||
|
89450ec1bd | ||
|
09f80cc842 | ||
|
fcb5c87ef0 | ||
|
bf0e480266 | ||
|
fce57ad8eb | ||
|
425fe7e55a | ||
|
5760864495 | ||
|
3d9c559cdb | ||
|
97a019fe25 | ||
|
8d5834232b | ||
|
ebc161e82f | ||
|
9485e53484 | ||
|
0d66f93ef3 | ||
|
ad80fab554 | ||
|
b7380018f1 | ||
|
2ea8e7fe6b | ||
|
e23e1affae | ||
|
8d8333b586 | ||
|
5712f5cd51 | ||
|
9cc15403c3 | ||
|
15fa7039e8 | ||
|
f83c820b38 | ||
|
323c9a74cc | ||
|
8a2d6eac9d | ||
|
61d8e28aad | ||
|
337ebd6f9f | ||
|
399dbf2b41 | ||
|
f952bcd298 | ||
|
9dc7c92c86 | ||
|
9e92201d82 | ||
|
4e592df6d8 |
31
.editorconfig
Normal file
31
.editorconfig
Normal file
@@ -0,0 +1,31 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.json]
|
||||
insert_final_newline = ignore
|
||||
|
||||
[*.js]
|
||||
indent_style = ignore
|
||||
insert_final_newline = ignore
|
||||
|
||||
[*.{md,txt}]
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
12
.gitignore
vendored
12
.gitignore
vendored
@@ -15,3 +15,15 @@ output-pwnagotchi
|
||||
build
|
||||
dist
|
||||
pwnagotchi.egg-info
|
||||
*backup*.tgz
|
||||
*backup*.gz
|
||||
.vscode
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
@@ -9,7 +9,7 @@ env:
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: vBUokTv94n8s65STUgTiD6I0Iy8KXbBRvQUrAof8XG+U4ZMsH5PmDTpS+wz+SaxI6o0PRkfyOiPVdARhiKAFnfatG3q9EHllMQwqRR2YIju51A3aCxgEJ5uWDoybwQdipERUMMYwUO/8XZaRRpwFD2bdQBFWkBtQyMcAkrEL8BXckwQQ531oDN2hK5gAiTllqsOswV2idwUlBRU9jOtStzff+UgUYsp/ZebsRodyOYkEB2Ev15yARo2HTXbyZ2icwHPtMbx5zmNUSRtxs9a4hfzaK3m6ctK8qLYYUdQvXub/ruuACapdw4Ez88LY1agTecbZhFYmJzv8oANH1e4VUI4owuHnZCpU6LRutS4wOhglrkOrGo6lSUlJeA+RtQjyjBugjej9DDtDyyIlRU1ZaBF3qWR9N5EXKuquf0olOfmUR67ap1NykE9VUpzkYjkoVRTiPs/e2onM/nRNOvAQcIt75FD13u+Y/DcYQ8r7KpMIu1HNdtbVx8gMeq76bRhP1YdDg2jm+DdJ21KWjf5QHsbyoXDfJzdKlCloLIlAU3EPJhMoXsnNzre0/FXeUl6dfteR1axNS6U7e/vKsQ9rlUFZWIQaeVPjfXmFKblNNVQ5uFrrsB/EGHcJl7IUx5fvcRT5hMMNwC660YxVkBXDbRb5fxMW5/+K0BOi9cP6en8=
|
||||
secure: "Rj0QnEDv02UzjKaxHHxJ/Sdj50EOFIrsKShr27GtVNSwHmNKxQuwlh31T0DQdif4wzcDtUZ5XWxr85vLkKJt1L8anb7Tb63qlu7Da69C+upSUow1uJkjsiScbMPqwHpwDIkkcUIbPsdbowI7qiNhRBD7nF8yyLPC5YLiU0cXKuGh4+Q5xdIlL3P7p8jm0919Y+olglzAZj0iNR/QxGOb+laNH8xi0oUsIPi5V0ZFfO/W/sm+nks9ki5nolfd1ML1gcbOD7uKuxIMTUrpDLl4p2Jx9IVQW+G2/tkmNLbP5Ga65NxQcfABQDYY3tCD8PsmFK9PEwa4cMbGJjqlo3yR7P21J5Aj3rK+L4KDntOvwem3Z3Y/v2JlQZn+gelhNFCxuPBi3ZihSf7POMHtpAYmi13N2ruzOg1ayjeYph0iN3vXIPs67DpAPaxK+8L2yoo6Nr/Cago9pGTkZoqS+J0fnWT31NXoYREPgg//L2+m42twQirFXttbhlGTBgNMLXpwcm8bZ2DW3pu3AEgVUxSoNAOjudoeyC0VzA6nUqe6STmfk06OYqcwM8q8NEyD62iAvUYU3Q7FnauZqcBqcP+ZYx82NPZybrQRX6YlJck5UomHbbEfjgpDFT+WvjrrfICmXH29YBOL1LWR4cKMT6RY58Cv8hT2PYxomB2I+DRrbqU="
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
file:
|
||||
|
9
Makefile
9
Makefile
@@ -1,10 +1,17 @@
|
||||
PACKER_VERSION=1.7.2
|
||||
PWN_HOSTNAME=pwnagotchi
|
||||
PWN_VERSION=master
|
||||
|
||||
all: clean install image
|
||||
|
||||
langs:
|
||||
@for lang in pwnagotchi/locale/*/; do\
|
||||
echo "compiling language: $$lang ..."; \
|
||||
./scripts/language.sh compile $$(basename $$lang); \
|
||||
done
|
||||
|
||||
install:
|
||||
curl https://releases.hashicorp.com/packer/1.3.5/packer_1.3.5_linux_amd64.zip -o /tmp/packer.zip
|
||||
curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip
|
||||
unzip /tmp/packer.zip -d /tmp
|
||||
sudo mv /tmp/packer /usr/bin/packer
|
||||
git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image
|
||||
|
@@ -15,7 +15,7 @@ full and half WPA handshakes.
|
||||
|
||||

|
||||
|
||||
Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.yml#L73) over time to **get better at pwning WiFi things to** in the environments you expose it to.
|
||||
Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.toml) over time to **get better at pwning WiFi things to** in the environments you expose it to.
|
||||
|
||||
More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://www.pwnagotchi.ai/usage/#training-the-ai) doc.)
|
||||
|
||||
|
218
bin/pwnagotchi
218
bin/pwnagotchi
@@ -1,24 +1,98 @@
|
||||
#!/usr/bin/python3
|
||||
import logging
|
||||
import argparse
|
||||
import time
|
||||
import signal
|
||||
import sys
|
||||
import toml
|
||||
|
||||
import pwnagotchi
|
||||
from pwnagotchi import utils
|
||||
from pwnagotchi.plugins import cmd as plugins_cmd
|
||||
from pwnagotchi import log
|
||||
from pwnagotchi import restart
|
||||
from pwnagotchi import fs
|
||||
from pwnagotchi.utils import DottedTomlEncoder
|
||||
|
||||
|
||||
def do_clear(display):
|
||||
logging.info("clearing the display ...")
|
||||
display.clear()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def do_manual_mode(agent):
|
||||
logging.info("entering manual mode ...")
|
||||
|
||||
agent.mode = 'manual'
|
||||
agent.last_session.parse(agent.view(), args.skip_session)
|
||||
if not args.skip_session:
|
||||
logging.info(
|
||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||
agent.last_session.duration_human,
|
||||
agent.last_session.epochs,
|
||||
agent.last_session.train_epochs,
|
||||
agent.last_session.avg_reward,
|
||||
agent.last_session.min_reward,
|
||||
agent.last_session.max_reward))
|
||||
|
||||
while True:
|
||||
display.on_manual_mode(agent.last_session)
|
||||
time.sleep(5)
|
||||
if grid.is_connected():
|
||||
plugins.on('internet_available', agent)
|
||||
|
||||
|
||||
def do_auto_mode(agent):
|
||||
logging.info("entering auto mode ...")
|
||||
|
||||
agent.mode = 'auto'
|
||||
agent.start()
|
||||
|
||||
while True:
|
||||
try:
|
||||
# recon on all channels
|
||||
agent.recon()
|
||||
# get nearby access points grouped by channel
|
||||
channels = agent.get_access_points_by_channel()
|
||||
# for each channel
|
||||
for ch, aps in channels:
|
||||
agent.set_channel(ch)
|
||||
|
||||
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()
|
||||
|
||||
if grid.is_connected():
|
||||
plugins.on('internet_available', agent)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("main loop exception (%s)", e)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
import time
|
||||
import logging
|
||||
import yaml
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.grid as grid
|
||||
import pwnagotchi.utils as utils
|
||||
import pwnagotchi.plugins as plugins
|
||||
|
||||
from pwnagotchi.identity import KeyPair
|
||||
from pwnagotchi.agent import Agent
|
||||
from pwnagotchi.ui.display import Display
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser = plugins_cmd.add_parsers(parser)
|
||||
|
||||
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.yml',
|
||||
parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml',
|
||||
help='Main configuration file.')
|
||||
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.yml',
|
||||
parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml',
|
||||
help='If this file exists, configuration will be merged and this will override default values.')
|
||||
|
||||
parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.")
|
||||
@@ -32,95 +106,61 @@ if __name__ == '__main__':
|
||||
help="Enable debug logs.")
|
||||
|
||||
parser.add_argument('--version', dest="version", action="store_true", default=False,
|
||||
help="Prints the version.")
|
||||
help="Print the version.")
|
||||
|
||||
parser.add_argument('--print-config', dest="print_config", action="store_true", default=False,
|
||||
help="Print the configuration.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
if plugins_cmd.used_plugin_cmd(args):
|
||||
config = utils.load_config(args)
|
||||
log.setup_logging(args, config)
|
||||
rc = plugins_cmd.handle_cmd(args, config)
|
||||
sys.exit(rc)
|
||||
|
||||
if args.version:
|
||||
print(pwnagotchi.version)
|
||||
SystemExit(0)
|
||||
print(pwnagotchi.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
config = utils.load_config(args)
|
||||
utils.setup_logging(args, config)
|
||||
|
||||
if args.print_config:
|
||||
print(toml.dumps(config, encoder=DottedTomlEncoder()))
|
||||
sys.exit(0)
|
||||
|
||||
from pwnagotchi.identity import KeyPair
|
||||
from pwnagotchi.agent import Agent
|
||||
from pwnagotchi.ui import fonts
|
||||
from pwnagotchi.ui.display import Display
|
||||
from pwnagotchi import grid
|
||||
from pwnagotchi import plugins
|
||||
|
||||
pwnagotchi.config = config
|
||||
fs.setup_mounts(config)
|
||||
log.setup_logging(args, config)
|
||||
fonts.init(config)
|
||||
|
||||
pwnagotchi.set_name(config['main']['name'])
|
||||
|
||||
plugins.load(config)
|
||||
|
||||
display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()})
|
||||
keypair = KeyPair(view=display)
|
||||
agent = Agent(view=display, config=config, keypair=keypair)
|
||||
|
||||
logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent.fingerprint(), pwnagotchi.version))
|
||||
|
||||
logging.debug("effective configuration:\n\n%s\n\n" % yaml.dump(config, default_flow_style=False))
|
||||
|
||||
for _, plugin in plugins.loaded.items():
|
||||
logging.debug("plugin '%s' v%s" % (plugin.__class__.__name__, plugin.__version__))
|
||||
|
||||
if args.do_clear:
|
||||
logging.info("clearing the display ...")
|
||||
display.clear()
|
||||
do_clear(display)
|
||||
sys.exit(0)
|
||||
|
||||
elif args.do_manual:
|
||||
logging.info("entering manual mode ...")
|
||||
agent = Agent(view=display, config=config, keypair=KeyPair(view=display))
|
||||
|
||||
agent.mode = 'manual'
|
||||
agent.last_session.parse(agent.view(), args.skip_session)
|
||||
if not args.skip_session:
|
||||
logging.info(
|
||||
"the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % (
|
||||
agent.last_session.duration_human,
|
||||
agent.last_session.epochs,
|
||||
agent.last_session.train_epochs,
|
||||
agent.last_session.avg_reward,
|
||||
agent.last_session.min_reward,
|
||||
agent.last_session.max_reward))
|
||||
def usr1_handler(*unused):
|
||||
logging.info('Received USR1 singal. Restart process ...')
|
||||
restart("MANU" if args.do_manual else "AUTO")
|
||||
|
||||
while True:
|
||||
display.on_manual_mode(agent.last_session)
|
||||
time.sleep(5)
|
||||
if grid.is_connected():
|
||||
plugins.on('internet_available', agent)
|
||||
signal.signal(signal.SIGUSR1, usr1_handler)
|
||||
|
||||
if args.do_manual:
|
||||
do_manual_mode(agent)
|
||||
else:
|
||||
logging.info("entering auto mode ...")
|
||||
|
||||
agent.mode = 'auto'
|
||||
agent.start()
|
||||
|
||||
while True:
|
||||
try:
|
||||
# recon on all channels
|
||||
agent.recon()
|
||||
# get nearby access points grouped by channel
|
||||
channels = agent.get_access_points_by_channel()
|
||||
# for each channel
|
||||
for ch, aps in channels:
|
||||
agent.set_channel(ch)
|
||||
|
||||
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()
|
||||
|
||||
if grid.is_connected():
|
||||
plugins.on('internet_available', agent)
|
||||
|
||||
except Exception as e:
|
||||
logging.exception("main loop exception")
|
||||
do_auto_mode(agent)
|
||||
|
29
builder/data/etc/bash_completion.d/pwnagotchi_completion.sh
Normal file
29
builder/data/etc/bash_completion.d/pwnagotchi_completion.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
_show_complete()
|
||||
{
|
||||
local cur opts node_names all_options opt_line
|
||||
all_options="
|
||||
pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config {plugins}
|
||||
pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade}
|
||||
pwnagotchi plugins list -i --installed -h --help
|
||||
pwnagotchi plugins install -h --help
|
||||
pwnagotchi plugins uninstall -h --help
|
||||
pwnagotchi plugins enable -h --help
|
||||
pwnagotchi plugins disable -h --help
|
||||
pwnagotchi plugins update -h --help
|
||||
pwnagotchi plugins upgrade -h --help
|
||||
"
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}"
|
||||
opt_line="$(grep -m1 "$cmd" <<<$all_options)"
|
||||
if [[ ${cur} == -* ]] ; then
|
||||
opts="$(echo $opt_line | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')"
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
fi
|
||||
|
||||
opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')"
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
}
|
||||
|
||||
complete -F _show_complete pwnagotchi
|
@@ -4,4 +4,5 @@ iface usb0 inet static
|
||||
netmask 255.255.255.0
|
||||
network 10.0.0.0
|
||||
broadcast 10.0.0.255
|
||||
gateway 10.0.0.1
|
||||
gateway 10.0.0.1
|
||||
metric 20
|
||||
|
@@ -6,10 +6,15 @@ After=pwngrid-peer.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/tmp
|
||||
PermissionsStartOnly=true
|
||||
ExecStart=/usr/bin/pwnagotchi-launcher
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
TasksMax=infinity
|
||||
LimitNPROC=infinity
|
||||
StandardOutput=null
|
||||
StandardError=null
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
@@ -1,6 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
# check if wifi driver is bugged
|
||||
if ! check_brcm; then
|
||||
if ! reload_brcm; then
|
||||
echo "Could not reload wifi driver. Reboot"
|
||||
reboot
|
||||
fi
|
||||
sleep 10
|
||||
fi
|
||||
|
||||
# start mon0
|
||||
start_monitor_interface
|
||||
|
||||
|
148
builder/data/usr/bin/decryption-webserver
Executable file
148
builder/data/usr/bin/decryption-webserver
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
|
||||
_HTML_FORM_TEMPLATE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Decryption</title>
|
||||
<style>
|
||||
body {{ text-align: center; padding: 150px; }}
|
||||
h1 {{ font-size: 50px; }}
|
||||
body {{ font: 20px Helvetica, sans-serif; color: #333; }}
|
||||
article {{ display: block; text-align: center; width: 650px; margin: 0 auto;}}
|
||||
input {{
|
||||
padding: 12px 20px;
|
||||
margin: 8px 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
}}
|
||||
input[type=password] {{
|
||||
width: 75%;
|
||||
font-size: 24px;
|
||||
}}
|
||||
input[type=submit] {{
|
||||
cursor: pointer;
|
||||
width: 75%;
|
||||
}}
|
||||
input[type=submit]:hover {{
|
||||
background-color: #d9d9d9;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<h1>Decryption</h1>
|
||||
<p>Some of your files are encrypted.</p>
|
||||
<p>Please provide the decryption password.</p>
|
||||
<div>
|
||||
<form action="/set-password" method="POST">
|
||||
{password_fields}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
POST_RESPONSE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
/* Center the loader */
|
||||
#loader {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
z-index: 1;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border: 16px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 16px solid #3498db;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
#myDiv {
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
|
||||
function checkPwnagotchi() {
|
||||
var target = 'http://' + document.location.hostname + ':8080/';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', target);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200 || xhr.status == 401) {
|
||||
window.location.replace(target);
|
||||
}else{
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
setTimeout(checkPwnagotchi, 1000);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="margin:0;">
|
||||
|
||||
<div id="loader"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
HTML_FORM = None
|
||||
|
||||
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(HTML_FORM.encode())
|
||||
|
||||
def do_POST(self):
|
||||
content_length = int(self.headers['Content-Length'])
|
||||
body = self.rfile.read(content_length)
|
||||
for mapping, password in parse_qsl(body.decode('UTF-8')):
|
||||
with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile:
|
||||
pwfile.write(password)
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(POST_RESPONSE.encode())
|
||||
|
||||
|
||||
with open('/root/.pwnagotchi-crypted') as crypted_file:
|
||||
mappings = [line.split()[0] for line in crypted_file.readlines()]
|
||||
fields = ''.join(['<label for="{m}">Passphrase for {m}:</label>\n<input type="password" id="{m}" name="{m}" value=""><br>'.format(m=m)
|
||||
for m in mappings])
|
||||
HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields)
|
||||
|
||||
httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler)
|
||||
httpd.serve_forever()
|
@@ -1,6 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
source /usr/bin/pwnlib
|
||||
|
||||
# we need to decrypt something
|
||||
if is_crypted_mode; then
|
||||
while ! is_decrypted; do
|
||||
echo "Waiting for decryption..."
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
# blink 10 times to signal ready state
|
||||
blink_led 10 &
|
||||
|
||||
@@ -8,4 +16,4 @@ if is_auto_mode; then
|
||||
/usr/local/bin/pwnagotchi
|
||||
else
|
||||
/usr/local/bin/pwnagotchi --manual
|
||||
fi
|
||||
fi
|
||||
|
@@ -12,9 +12,28 @@ blink_led() {
|
||||
sleep 0.3
|
||||
}
|
||||
|
||||
# check if brcm is stuck
|
||||
check_brcm() {
|
||||
if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# reload mod
|
||||
reload_brcm() {
|
||||
if ! modprobe -r brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
if ! modprobe brcmfmac; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# starts mon0
|
||||
start_monitor_interface() {
|
||||
iw phy phy0 interface add mon0 type monitor && ifconfig mon0 up
|
||||
iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add mon0 type monitor && ifconfig mon0 up
|
||||
}
|
||||
|
||||
# stops mon0
|
||||
@@ -84,4 +103,82 @@ is_auto_mode_no_delete() {
|
||||
|
||||
# no override, but none of the interfaces is up -> AUTO
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
# check if we need to decrypt something
|
||||
is_crypted_mode() {
|
||||
if [ -f /root/.pwnagotchi-crypted ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# decryption loop
|
||||
is_decrypted() {
|
||||
while read -r mapping container mount; do
|
||||
# mapping = name the device or file will be mapped to
|
||||
# container = the luks encrypted device or file
|
||||
# mount = the mountpoint
|
||||
|
||||
# fail if not mounted
|
||||
if ! mountpoint -q "$mount" >/dev/null 2>&1; then
|
||||
if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then
|
||||
</tmp/.pwnagotchi-secret-"$mapping" read -r SECRET
|
||||
if ! test -b /dev/disk/by-id/dm-uuid-*"$(cryptsetup luksUUID "$container" | tr -d -)"*; then
|
||||
if echo -n "$SECRET" | cryptsetup luksOpen -d- "$container" "$mapping" >/dev/null 2>&1; then
|
||||
echo "Container decrypted!"
|
||||
fi
|
||||
fi
|
||||
|
||||
if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then
|
||||
echo "Mounted /dev/mapper/$mapping to $mount"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0
|
||||
fi
|
||||
|
||||
if ! pgrep -f decryption-webserver >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 decryption-webserver &
|
||||
fi
|
||||
|
||||
if ! pgrep wpa_supplicant >/dev/null 2>&1; then
|
||||
>/tmp/wpa_supplicant.conf cat <<EOF
|
||||
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
|
||||
update_config=1
|
||||
ap_scan=2
|
||||
|
||||
network={
|
||||
ssid="DECRYPT-ME"
|
||||
mode=2
|
||||
key_mgmt=WPA-PSK
|
||||
psk="pwnagotchi"
|
||||
frequency=2437
|
||||
}
|
||||
EOF
|
||||
>/dev/null 2>&1 wpa_supplicant -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf &
|
||||
fi
|
||||
|
||||
if ! pgrep dnsmasq >/dev/null 2>&1; then
|
||||
>/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h &
|
||||
fi
|
||||
|
||||
return 1
|
||||
fi
|
||||
done </root/.pwnagotchi-crypted
|
||||
|
||||
# overwrite passwords
|
||||
python3 -c 'print("A"*4096)' | tee /tmp/.pwnagotchi-secret-* >/dev/null
|
||||
# delete
|
||||
rm /tmp/.pwnagotchi-secret-*
|
||||
sync # flush
|
||||
|
||||
pkill wpa_supplicant
|
||||
pkill dnsmasq
|
||||
pid="$(pgrep -f "decryption-webserver")"
|
||||
[[ -n "$pid" ]] && kill "$pid"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@
|
||||
"name": "pwnagotchi",
|
||||
"type": "arm-image",
|
||||
"iso_url": "https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-07-12/2019-07-10-raspbian-buster-lite.zip",
|
||||
"iso_checksum_type": "sha256",
|
||||
"iso_checksum": "9e5cf24ce483bb96e7736ea75ca422e3560e7b455eee63dd28f66fa1825db70e",
|
||||
"last_partition_extra_size": 3221225472
|
||||
}
|
||||
@@ -98,6 +97,7 @@
|
||||
{
|
||||
"type": "ansible-local",
|
||||
"playbook_file": "pwnagotchi.yml",
|
||||
"extra_arguments": [ "--extra-vars \"ansible_python_interpreter=/usr/bin/python3\"" ],
|
||||
"command": "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION={{user `pwn_version`}} PWN_HOSTNAME={{user `pwn_hostname`}} ansible-playbook"
|
||||
},
|
||||
{
|
||||
|
@@ -23,6 +23,7 @@
|
||||
- bettercap.service
|
||||
- pwngrid-peer.service
|
||||
- epd-fuse.service
|
||||
- fstrim.timer
|
||||
disable:
|
||||
- apt-daily.timer
|
||||
- apt-daily.service
|
||||
@@ -34,10 +35,10 @@
|
||||
- ifup@wlan0.service
|
||||
packages:
|
||||
bettercap:
|
||||
url: "https://github.com/bettercap/bettercap/releases/download/v2.26.1/bettercap_linux_armhf_v2.26.1.zip"
|
||||
url: "https://github.com/bettercap/bettercap/releases/download/v2.31.0/bettercap_linux_armhf_v2.31.0.zip"
|
||||
ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip"
|
||||
pwngrid:
|
||||
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.1/pwngrid_linux_armhf_v1.10.1.zip"
|
||||
url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.3/pwngrid_linux_armhf_v1.10.3.zip"
|
||||
apt:
|
||||
hold:
|
||||
- firmware-atheros
|
||||
@@ -46,12 +47,13 @@
|
||||
- firmware-misc-nonfree
|
||||
- firmware-realtek
|
||||
remove:
|
||||
- rasberrypi-net-mods
|
||||
- raspberrypi-net-mods
|
||||
- dhcpcd5
|
||||
- triggerhappy
|
||||
- wpa_supplicant
|
||||
- nfs-common
|
||||
install:
|
||||
- rsync
|
||||
- vim
|
||||
- screen
|
||||
- golang
|
||||
@@ -99,9 +101,9 @@
|
||||
- bc
|
||||
- fonts-freefont-ttf
|
||||
- fbi
|
||||
- python3-flask
|
||||
- python3-flask-cors
|
||||
- python3-flaskext.wtf
|
||||
- fonts-ipaexfont-gothic
|
||||
- cryptsetup
|
||||
- dnsmasq
|
||||
|
||||
tasks:
|
||||
- name: change hostname
|
||||
@@ -176,7 +178,7 @@
|
||||
chdir: /usr/local/src/gratis
|
||||
target: rpi
|
||||
params:
|
||||
EPD_IO: epd_io.h
|
||||
EPD_IO: epd_io_free_uart.h
|
||||
PANEL_VERSION: 'V231_G2'
|
||||
when: gratisgit.changed
|
||||
|
||||
@@ -185,7 +187,7 @@
|
||||
chdir: /usr/local/src/gratis
|
||||
target: rpi-install
|
||||
params:
|
||||
EPD_IO: epd_io.h
|
||||
EPD_IO: epd_io_free_uart.h
|
||||
PANEL_VERSION: 'V231_G2'
|
||||
when: gratisgit.changed
|
||||
|
||||
@@ -215,9 +217,19 @@
|
||||
dest: /usr/local/src/pwnagotchi
|
||||
register: pwnagotchigit
|
||||
|
||||
- name: create /usr/local/share/pwnagotchi/ folder
|
||||
file:
|
||||
path: /usr/local/share/pwnagotchi/
|
||||
state: directory
|
||||
|
||||
- name: clone pwnagotchi plugins repository
|
||||
git:
|
||||
repo: https://github.com/evilsocket/pwnagotchi-plugins-contrib.git
|
||||
dest: /usr/local/share/pwnagotchi/availaible-plugins
|
||||
|
||||
- name: fetch pwnagotchi version
|
||||
set_fact:
|
||||
pwnagotchi_version: "{{ lookup('file', '/usr/local/src/pwnagotchi/pwnagotchi/__init__.py') | replace('\n', ' ') | regex_replace('.*version.*=.*''([0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]*)''.*', '\\1') }}"
|
||||
pwnagotchi_version: "{{ lookup('file', '/usr/local/src/pwnagotchi/pwnagotchi/_version.py') | regex_replace('.*__version__.*=.*''([0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]*)''.*', '\\1') }}"
|
||||
|
||||
- name: pwnagotchi version found
|
||||
debug:
|
||||
@@ -227,17 +239,17 @@
|
||||
command: "python3 setup.py sdist bdist_wheel"
|
||||
args:
|
||||
chdir: /usr/local/src/pwnagotchi
|
||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi.version)
|
||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
||||
|
||||
- name: install opencv-python
|
||||
pip:
|
||||
name: "https://www.piwheels.hostedpi.com/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl"
|
||||
name: "https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl"
|
||||
extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}"
|
||||
when: (pip_packages['opencv-python'] is undefined) or (pip_packages['opencv-python'] != '3.4.3.18')
|
||||
|
||||
- name: install tensorflow
|
||||
pip:
|
||||
name: "https://www.piwheels.hostedpi.com/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
|
||||
name: "https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl"
|
||||
extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}"
|
||||
when: (pip_packages['tensorflow'] is undefined) or (pip_packages['tensorflow'] != '1.13.1')
|
||||
|
||||
@@ -245,7 +257,7 @@
|
||||
pip:
|
||||
name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}"
|
||||
extra_args: "--no-cache-dir"
|
||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi.version)
|
||||
when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version)
|
||||
|
||||
- name: download and install pwngrid
|
||||
unarchive:
|
||||
@@ -299,21 +311,17 @@
|
||||
|
||||
- name: check if user configuration exists
|
||||
stat:
|
||||
path: /etc/pwnagotchi/config.yml
|
||||
path: /etc/pwnagotchi/config.toml
|
||||
register: user_config
|
||||
|
||||
- name: create /etc/pwnagotchi/config.yml
|
||||
- name: create /etc/pwnagotchi/config.toml
|
||||
copy:
|
||||
dest: /etc/pwnagotchi/config.yml
|
||||
dest: /etc/pwnagotchi/config.toml
|
||||
content: |
|
||||
# Add your configuration overrides on this file any configuration changes done to default.yml will be lost!
|
||||
# Add your configuration overrides on this file any configuration changes done to default.toml will be lost!
|
||||
# Example:
|
||||
#
|
||||
# ui:
|
||||
# display:
|
||||
# type: 'inkyphat'
|
||||
# color: 'black'
|
||||
#
|
||||
# ui.display.enabled = true
|
||||
# ui.display.type = "waveshare_2"
|
||||
when: not user_config.stat.exists
|
||||
|
||||
- name: enable ssh on boot
|
||||
@@ -360,15 +368,15 @@
|
||||
Hi! I'm a pwnagotchi, please take good care of me!
|
||||
Here are some basic things you need to know to raise me properly!
|
||||
|
||||
If you want to change my configuration, use /etc/pwnagotchi/config.yml
|
||||
If you want to change my configuration, use /etc/pwnagotchi/config.toml
|
||||
|
||||
All the configuration options can be found on /etc/pwnagotchi/default.yml,
|
||||
All the configuration options can be found on /etc/pwnagotchi/default.toml,
|
||||
but don't change this file because I will recreate it every time I'm restarted!
|
||||
|
||||
I'm managed by systemd. Here are some basic commands.
|
||||
|
||||
If you want to know what I'm doing, you can check my logs with the command
|
||||
journalctl -fu pwnagotchi
|
||||
tail -f /var/log/pwnagotchi.log
|
||||
|
||||
If you want to know if I'm running, you can use
|
||||
systemctl status pwnagotchi
|
||||
|
@@ -1,14 +1,14 @@
|
||||
import subprocess
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
import re
|
||||
import pwnagotchi.ui.view as view
|
||||
import pwnagotchi
|
||||
|
||||
version = '1.2.0'
|
||||
|
||||
|
||||
from pwnagotchi._version import __version__
|
||||
|
||||
_name = None
|
||||
config = None
|
||||
|
||||
|
||||
def set_name(new_name):
|
||||
@@ -27,17 +27,17 @@ def set_name(new_name):
|
||||
if new_name != current:
|
||||
global _name
|
||||
|
||||
logging.info("setting unit hostname '%s' -> '%s'" % (current, new_name))
|
||||
logging.info("setting unit hostname '%s' -> '%s'", current, new_name)
|
||||
with open('/etc/hostname', 'wt') as fp:
|
||||
fp.write(new_name)
|
||||
|
||||
with open('/etc/hosts', 'rt') as fp:
|
||||
prev = fp.read()
|
||||
logging.debug("old hosts:\n%s\n" % prev)
|
||||
logging.debug("old hosts:\n%s\n", prev)
|
||||
|
||||
with open('/etc/hosts', 'wt') as fp:
|
||||
patched = prev.replace(current, new_name, -1)
|
||||
logging.debug("new hosts:\n%s\n" % patched)
|
||||
logging.debug("new hosts:\n%s\n", patched)
|
||||
fp.write(patched)
|
||||
|
||||
os.system("hostname '%s'" % new_name)
|
||||
@@ -65,8 +65,6 @@ def mem_usage():
|
||||
kb_mem_total = int(line.split()[1])
|
||||
if line.startswith("MemFree:"):
|
||||
kb_mem_free = int(line.split()[1])
|
||||
if line.startswith("MemAvailable:"):
|
||||
kb_mem_available = int(line.split()[1])
|
||||
if line.startswith("Buffers:"):
|
||||
kb_main_buffers = int(line.split()[1])
|
||||
if line.startswith("Cached:"):
|
||||
@@ -77,18 +75,27 @@ def mem_usage():
|
||||
return 0
|
||||
|
||||
|
||||
def cpu_load():
|
||||
def _cpu_stat():
|
||||
"""
|
||||
Returns the splitted first line of the /proc/stat file
|
||||
"""
|
||||
with open('/proc/stat', 'rt') as fp:
|
||||
for line in fp:
|
||||
line = line.strip()
|
||||
if line.startswith('cpu '):
|
||||
parts = list(map(int, line.split()[1:]))
|
||||
user_n = parts[0]
|
||||
sys_n = parts[2]
|
||||
idle_n = parts[3]
|
||||
tot = user_n + sys_n + idle_n
|
||||
return (user_n + sys_n) / tot
|
||||
return 0
|
||||
return list(map(int,fp.readline().split()[1:]))
|
||||
|
||||
|
||||
def cpu_load():
|
||||
"""
|
||||
Returns the current cpuload
|
||||
"""
|
||||
parts0 = _cpu_stat()
|
||||
time.sleep(0.1)
|
||||
parts1 = _cpu_stat()
|
||||
parts_diff = [p1 - p0 for (p0, p1) in zip(parts0, parts1)]
|
||||
user, nice, sys, idle, iowait, irq, softirq, steal, _guest, _guest_nice = parts_diff
|
||||
idle_sum = idle + iowait
|
||||
non_idle_sum = user + nice + sys + irq + softirq + steal
|
||||
total = idle_sum + non_idle_sum
|
||||
return non_idle_sum / total
|
||||
|
||||
|
||||
def temperature(celsius=True):
|
||||
@@ -100,16 +107,25 @@ def temperature(celsius=True):
|
||||
|
||||
def shutdown():
|
||||
logging.warning("shutting down ...")
|
||||
|
||||
from pwnagotchi.ui import view
|
||||
if view.ROOT:
|
||||
view.ROOT.on_shutdown()
|
||||
# give it some time to refresh the ui
|
||||
time.sleep(10)
|
||||
|
||||
logging.warning("syncing...")
|
||||
|
||||
from pwnagotchi import fs
|
||||
for m in fs.mounts:
|
||||
m.sync()
|
||||
|
||||
os.system("sync")
|
||||
os.system("halt")
|
||||
|
||||
|
||||
def restart(mode):
|
||||
logging.warning("restarting in %s mode ..." % mode)
|
||||
logging.warning("restarting in %s mode ...", mode)
|
||||
|
||||
if mode == 'AUTO':
|
||||
os.system("touch /root/.pwnagotchi-auto")
|
||||
@@ -123,12 +139,13 @@ def restart(mode):
|
||||
def reboot(mode=None):
|
||||
if mode is not None:
|
||||
mode = mode.upper()
|
||||
logging.warning("rebooting in %s mode ..." % mode)
|
||||
logging.warning("rebooting in %s mode ...", mode)
|
||||
else:
|
||||
logging.warning("rebooting ...")
|
||||
|
||||
from pwnagotchi.ui import view
|
||||
if view.ROOT:
|
||||
view.ROOT.on_reboot()
|
||||
view.ROOT.on_rebooting()
|
||||
# give it some time to refresh the ui
|
||||
time.sleep(10)
|
||||
|
||||
@@ -137,5 +154,11 @@ def reboot(mode=None):
|
||||
elif mode == 'MANU':
|
||||
os.system("touch /root/.pwnagotchi-manual")
|
||||
|
||||
logging.warning("syncing...")
|
||||
|
||||
from pwnagotchi import fs
|
||||
for m in fs.mounts:
|
||||
m.sync()
|
||||
|
||||
os.system("sync")
|
||||
os.system("shutdown -r now")
|
||||
|
1
pwnagotchi/_version.py
Normal file
1
pwnagotchi/_version.py
Normal file
@@ -0,0 +1 @@
|
||||
__version__ = '1.5.5'
|
@@ -3,6 +3,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
import asyncio
|
||||
import _thread
|
||||
|
||||
import pwnagotchi
|
||||
@@ -30,12 +31,14 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
AsyncTrainer.__init__(self, config)
|
||||
|
||||
self._started_at = time.time()
|
||||
self._filter = None if config['main']['filter'] is None else re.compile(config['main']['filter'])
|
||||
self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter'])
|
||||
self._current_channel = 0
|
||||
self._tot_aps = 0
|
||||
self._aps_on_channel = 0
|
||||
self._supported_channels = utils.iface_channels(config['main']['iface'])
|
||||
self._view = view
|
||||
self._view.set_agent(self)
|
||||
self._web_ui = Server(self, self._config['ui']['display'])
|
||||
self._web_ui = Server(self, config['ui'])
|
||||
|
||||
self._access_points = []
|
||||
self._last_pwnd = None
|
||||
@@ -47,6 +50,10 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
if not os.path.exists(config['bettercap']['handshakes']):
|
||||
os.makedirs(config['bettercap']['handshakes'])
|
||||
|
||||
logging.info("%s@%s (v%s)", pwnagotchi.name(), self.fingerprint(), pwnagotchi.__version__)
|
||||
for _, plugin in plugins.loaded.items():
|
||||
logging.debug("plugin '%s' v%s", plugin.__class__.__name__, plugin.__version__)
|
||||
|
||||
def config(self):
|
||||
return self._config
|
||||
|
||||
@@ -57,12 +64,12 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
return self._supported_channels
|
||||
|
||||
def setup_events(self):
|
||||
logging.info("connecting to %s ..." % self.url)
|
||||
logging.info("connecting to %s ...", self.url)
|
||||
|
||||
for tag in self._config['bettercap']['silence']:
|
||||
try:
|
||||
self.run('events.ignore %s' % tag, verbose_errors=False)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _reset_wifi_settings(self):
|
||||
@@ -84,7 +91,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
s = self.session()
|
||||
for iface in s['interfaces']:
|
||||
if iface['name'] == mon_iface:
|
||||
logging.info("found monitor interface: %s" % iface['name'])
|
||||
logging.info("found monitor interface: %s", iface['name'])
|
||||
has_mon = True
|
||||
break
|
||||
|
||||
@@ -93,11 +100,11 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
logging.info("starting monitor interface ...")
|
||||
self.run('!%s' % mon_start_cmd)
|
||||
else:
|
||||
logging.info("waiting for monitor interface %s ..." % mon_iface)
|
||||
logging.info("waiting for monitor interface %s ...", mon_iface)
|
||||
time.sleep(1)
|
||||
|
||||
logging.info("supported channels: %s" % self._supported_channels)
|
||||
logging.info("handshakes will be collected inside %s" % self._config['bettercap']['handshakes'])
|
||||
logging.info("supported channels: %s", self._supported_channels)
|
||||
logging.info("handshakes will be collected inside %s", self._config['bettercap']['handshakes'])
|
||||
|
||||
self._reset_wifi_settings()
|
||||
|
||||
@@ -115,9 +122,9 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
def _wait_bettercap(self):
|
||||
while True:
|
||||
try:
|
||||
s = self.session()
|
||||
_s = self.session()
|
||||
return
|
||||
except:
|
||||
except Exception:
|
||||
logging.info("waiting for bettercap API to be available ...")
|
||||
time.sleep(1)
|
||||
|
||||
@@ -128,6 +135,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
self.set_starting()
|
||||
self.start_monitor_mode()
|
||||
self.start_event_polling()
|
||||
self.start_session_fetcher()
|
||||
# print initial stats
|
||||
self.next_epoch()
|
||||
self.set_ready()
|
||||
@@ -145,14 +153,14 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
if not channels:
|
||||
self._current_channel = 0
|
||||
logging.debug("RECON %ds" % recon_time)
|
||||
logging.debug("RECON %ds", recon_time)
|
||||
self.run('wifi.recon.channel clear')
|
||||
else:
|
||||
logging.debug("RECON %ds ON CHANNELS %s" % (recon_time, ','.join(map(str, channels))))
|
||||
logging.debug("RECON %ds ON CHANNELS %s", recon_time, ','.join(map(str, channels)))
|
||||
try:
|
||||
self.run('wifi.recon.channel %s' % ','.join(map(str, channels)))
|
||||
except Exception as e:
|
||||
logging.exception("error")
|
||||
logging.exception("Error while setting wifi.recon.channels (%s)", e)
|
||||
|
||||
self.wait_for(recon_time, sleeping=False)
|
||||
|
||||
@@ -176,15 +184,26 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
for ap in s['wifi']['aps']:
|
||||
if ap['encryption'] == '' or ap['encryption'] == 'OPEN':
|
||||
continue
|
||||
elif ap['hostname'] not in whitelist:
|
||||
elif ap['hostname'] not in whitelist \
|
||||
and ap['mac'].lower() not in whitelist \
|
||||
and ap['mac'][:8].lower() not in whitelist:
|
||||
if self._filter_included(ap):
|
||||
aps.append(ap)
|
||||
except Exception as e:
|
||||
logging.exception("error")
|
||||
logging.exception("Error while getting acces points (%s)", e)
|
||||
|
||||
aps.sort(key=lambda ap: ap['channel'])
|
||||
return self.set_access_points(aps)
|
||||
|
||||
def get_total_aps(self):
|
||||
return self._tot_aps
|
||||
|
||||
def get_aps_on_channel(self):
|
||||
return self._aps_on_channel
|
||||
|
||||
def get_current_channel(self):
|
||||
return self._current_channel
|
||||
|
||||
def get_access_points_by_channel(self):
|
||||
aps = self.get_access_points()
|
||||
channels = self._config['personality']['channels']
|
||||
@@ -195,7 +214,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
ch = ap['channel']
|
||||
# if we're sticking to a channel, skip anything
|
||||
# which is not on that channel
|
||||
if channels != [] and ch not in channels:
|
||||
if channels and ch not in channels:
|
||||
continue
|
||||
|
||||
if ch not in grouped:
|
||||
@@ -221,16 +240,16 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
# self._view.set('epoch', '%04d' % self._epoch.epoch)
|
||||
|
||||
def _update_counters(self):
|
||||
tot_aps = len(self._access_points)
|
||||
self._tot_aps = len(self._access_points)
|
||||
tot_stas = sum(len(ap['clients']) for ap in self._access_points)
|
||||
if self._current_channel == 0:
|
||||
self._view.set('aps', '%d' % tot_aps)
|
||||
self._view.set('aps', '%d' % self._tot_aps)
|
||||
self._view.set('sta', '%d' % tot_stas)
|
||||
else:
|
||||
aps_on_channel = len([ap for ap in self._access_points if ap['channel'] == self._current_channel])
|
||||
self._aps_on_channel = len([ap for ap in self._access_points if ap['channel'] == self._current_channel])
|
||||
stas_on_channel = sum(
|
||||
[len(ap['clients']) for ap in self._access_points if ap['channel'] == self._current_channel])
|
||||
self._view.set('aps', '%d (%d)' % (aps_on_channel, tot_aps))
|
||||
self._view.set('aps', '%d (%d)' % (self._aps_on_channel, self._tot_aps))
|
||||
self._view.set('sta', '%d (%d)' % (stas_on_channel, tot_stas))
|
||||
|
||||
def _update_handshakes(self, new_shakes=0):
|
||||
@@ -257,7 +276,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
pwnagotchi.reboot()
|
||||
|
||||
def _save_recovery_data(self):
|
||||
logging.warning("writing recovery data to %s ..." % RECOVERY_DATA_FILE)
|
||||
logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE)
|
||||
with open(RECOVERY_DATA_FILE, 'w') as fp:
|
||||
data = {
|
||||
'started_at': self._started_at,
|
||||
@@ -272,7 +291,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
try:
|
||||
with open(RECOVERY_DATA_FILE, 'rt') as fp:
|
||||
data = json.load(fp)
|
||||
logging.info("found recovery data: %s" % data)
|
||||
logging.info("found recovery data: %s", data)
|
||||
self._started_at = data['started_at']
|
||||
self._epoch.epoch = data['epoch']
|
||||
self._handshakes = data['handshakes']
|
||||
@@ -280,64 +299,74 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
self._last_pwnd = data['last_pwnd']
|
||||
|
||||
if delete:
|
||||
logging.info("deleting %s" % RECOVERY_DATA_FILE)
|
||||
logging.info("deleting %s", RECOVERY_DATA_FILE)
|
||||
os.unlink(RECOVERY_DATA_FILE)
|
||||
except:
|
||||
if not no_exceptions:
|
||||
raise
|
||||
|
||||
def _event_poller(self):
|
||||
self._load_recovery_data()
|
||||
|
||||
def start_session_fetcher(self):
|
||||
_thread.start_new_thread(self._fetch_stats, ())
|
||||
|
||||
|
||||
def _fetch_stats(self):
|
||||
while True:
|
||||
s = self.session()
|
||||
self._update_uptime(s)
|
||||
self._update_advertisement(s)
|
||||
self._update_peers()
|
||||
self._update_counters()
|
||||
self._update_handshakes(0)
|
||||
time.sleep(1)
|
||||
|
||||
async def _on_event(self, msg):
|
||||
found_handshake = False
|
||||
jmsg = json.loads(msg)
|
||||
|
||||
if jmsg['tag'] == 'wifi.client.handshake':
|
||||
filename = jmsg['data']['file']
|
||||
sta_mac = jmsg['data']['station']
|
||||
ap_mac = jmsg['data']['ap']
|
||||
key = "%s -> %s" % (sta_mac, ap_mac)
|
||||
if key not in self._handshakes:
|
||||
self._handshakes[key] = jmsg
|
||||
s = self.session()
|
||||
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
|
||||
if ap_and_station is None:
|
||||
logging.warning("!!! captured new handshake: %s !!!", key)
|
||||
self._last_pwnd = ap_mac
|
||||
plugins.on('handshake', self, filename, ap_mac, sta_mac)
|
||||
else:
|
||||
(ap, sta) = ap_and_station
|
||||
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[
|
||||
'hostname'] != '<hidden>' else ap_mac
|
||||
logging.warning(
|
||||
"!!! captured new handshake on channel %d, %d dBm: %s (%s) -> %s [%s (%s)] !!!",
|
||||
ap['channel'],
|
||||
ap['rssi'],
|
||||
sta['mac'], sta['vendor'],
|
||||
ap['hostname'], ap['mac'], ap['vendor'])
|
||||
plugins.on('handshake', self, filename, ap, sta)
|
||||
found_handshake = True
|
||||
self._update_handshakes(1 if found_handshake else 0)
|
||||
|
||||
def _event_poller(self, loop):
|
||||
self._load_recovery_data()
|
||||
self.run('events.clear')
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
new_shakes = 0
|
||||
|
||||
logging.debug("polling events ...")
|
||||
|
||||
try:
|
||||
s = self.session()
|
||||
self._update_uptime(s)
|
||||
|
||||
self._update_advertisement(s)
|
||||
self._update_peers()
|
||||
self._update_counters()
|
||||
|
||||
for h in [e for e in self.events() if e['tag'] == 'wifi.client.handshake']:
|
||||
filename = h['data']['file']
|
||||
sta_mac = h['data']['station']
|
||||
ap_mac = h['data']['ap']
|
||||
key = "%s -> %s" % (sta_mac, ap_mac)
|
||||
|
||||
if key not in self._handshakes:
|
||||
self._handshakes[key] = h
|
||||
new_shakes += 1
|
||||
ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s)
|
||||
if ap_and_station is None:
|
||||
logging.warning("!!! captured new handshake: %s !!!" % key)
|
||||
self._last_pwnd = ap_mac
|
||||
plugins.on('handshake', self, filename, ap_mac, sta_mac)
|
||||
else:
|
||||
(ap, sta) = ap_and_station
|
||||
self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[
|
||||
'hostname'] != '<hidden>' else ap_mac
|
||||
logging.warning("!!! captured new handshake on channel %d: %s (%s) -> %s [%s (%s)] !!!" % ( \
|
||||
ap['channel'],
|
||||
sta['mac'], sta['vendor'],
|
||||
ap['hostname'], ap['mac'], ap['vendor']))
|
||||
plugins.on('handshake', self, filename, ap, sta)
|
||||
|
||||
except Exception as e:
|
||||
logging.error("error: %s" % e)
|
||||
|
||||
finally:
|
||||
self._update_handshakes(new_shakes)
|
||||
loop.create_task(self.start_websocket(self._on_event))
|
||||
loop.run_forever()
|
||||
except Exception as ex:
|
||||
logging.debug("Error while polling via websocket (%s)", ex)
|
||||
|
||||
def start_event_polling(self):
|
||||
_thread.start_new_thread(self._event_poller, ())
|
||||
# start a thread and pass in the mainloop
|
||||
_thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),))
|
||||
|
||||
|
||||
def is_module_running(self, module):
|
||||
s = self.session()
|
||||
@@ -373,15 +402,15 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def associate(self, ap, throttle=0):
|
||||
if self.is_stale():
|
||||
logging.debug("recon is stale, skipping assoc(%s)" % ap['mac'])
|
||||
logging.debug("recon is stale, skipping assoc(%s)", ap['mac'])
|
||||
return
|
||||
|
||||
if self._config['personality']['associate'] and self._should_interact(ap['mac']):
|
||||
self._view.on_assoc(ap)
|
||||
|
||||
try:
|
||||
logging.info("sending association frame to %s (%s %s) on channel %d [%d clients]..." % ( \
|
||||
ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients'])))
|
||||
logging.info("sending association frame to %s (%s %s) on channel %d [%d clients], %d dBm...",
|
||||
ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients']), ap['rssi'])
|
||||
self.run('wifi.assoc %s' % ap['mac'])
|
||||
self._epoch.track(assoc=True)
|
||||
except Exception as e:
|
||||
@@ -394,15 +423,15 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def deauth(self, ap, sta, throttle=0):
|
||||
if self.is_stale():
|
||||
logging.debug("recon is stale, skipping deauth(%s)" % sta['mac'])
|
||||
logging.debug("recon is stale, skipping deauth(%s)", sta['mac'])
|
||||
return
|
||||
|
||||
if self._config['personality']['deauth'] and self._should_interact(sta['mac']):
|
||||
self._view.on_deauth(sta)
|
||||
|
||||
try:
|
||||
logging.info("deauthing %s (%s) from %s (%s %s) on channel %d ..." % (
|
||||
sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel']))
|
||||
logging.info("deauthing %s (%s) from %s (%s %s) on channel %d, %d dBm ...",
|
||||
sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], ap['rssi'])
|
||||
self.run('wifi.deauth %s' % sta['mac'])
|
||||
self._epoch.track(deauth=True)
|
||||
except Exception as e:
|
||||
@@ -415,7 +444,7 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
|
||||
def set_channel(self, channel, verbose=True):
|
||||
if self.is_stale():
|
||||
logging.debug("recon is stale, skipping set_channel(%d)" % channel)
|
||||
logging.debug("recon is stale, skipping set_channel(%d)", channel)
|
||||
return
|
||||
|
||||
# if in the previous loop no client stations has been deauthenticated
|
||||
@@ -431,12 +460,12 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
if channel != self._current_channel:
|
||||
if self._current_channel != 0 and wait > 0:
|
||||
if verbose:
|
||||
logging.info("waiting for %ds on channel %d ..." % (wait, self._current_channel))
|
||||
logging.info("waiting for %ds on channel %d ...", wait, self._current_channel)
|
||||
else:
|
||||
logging.debug("waiting for %ds on channel %d ..." % (wait, self._current_channel))
|
||||
logging.debug("waiting for %ds on channel %d ...", wait, self._current_channel)
|
||||
self.wait_for(wait)
|
||||
if verbose and self._epoch.any_activity:
|
||||
logging.info("CHANNEL %d" % channel)
|
||||
logging.info("CHANNEL %d", channel)
|
||||
try:
|
||||
self.run('wifi.recon.channel %d' % channel)
|
||||
self._current_channel = channel
|
||||
@@ -446,4 +475,4 @@ class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer):
|
||||
plugins.on('channel_hop', self, channel)
|
||||
|
||||
except Exception as e:
|
||||
logging.error("error: %s" % e)
|
||||
logging.error("Error while setting channel (%s)", e)
|
||||
|
@@ -1,12 +1,9 @@
|
||||
import os
|
||||
import time
|
||||
import warnings
|
||||
import logging
|
||||
|
||||
# https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints/40426709
|
||||
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'}
|
||||
# https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning
|
||||
warnings.simplefilter(action='ignore', category=FutureWarning)
|
||||
|
||||
|
||||
def load(config, agent, epoch, from_disk=True):
|
||||
@@ -59,7 +56,7 @@ def load(config, agent, epoch, from_disk=True):
|
||||
|
||||
return a2c
|
||||
except Exception as e:
|
||||
logging.exception("error while starting AI")
|
||||
logging.exception("error while starting AI (%s)", e)
|
||||
|
||||
logging.warning("[ai] AI not loaded!")
|
||||
return False
|
||||
|
@@ -19,6 +19,10 @@ class Epoch(object):
|
||||
self.active_for = 0
|
||||
# number of epochs with no visible access points
|
||||
self.blind_for = 0
|
||||
# number of epochs in sad state
|
||||
self.sad_for = 0
|
||||
# number of epochs in bored state
|
||||
self.bored_for = 0
|
||||
# did deauth in this epoch in the current channel?
|
||||
self.did_deauth = False
|
||||
# number of deauths in this epoch
|
||||
@@ -99,13 +103,13 @@ class Epoch(object):
|
||||
try:
|
||||
aps_per_chan[ch_idx] += 1.0
|
||||
sta_per_chan[ch_idx] += len(ap['clients'])
|
||||
except IndexError as e:
|
||||
except IndexError:
|
||||
logging.error("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels))
|
||||
|
||||
for peer in peers:
|
||||
try:
|
||||
peers_per_chan[peer.last_channel - 1] += 1.0
|
||||
except IndexError as e:
|
||||
except IndexError:
|
||||
logging.error(
|
||||
"got peer data on channel %d, we can store %d channels" % (peer.last_channel, wifi.NumChannels))
|
||||
|
||||
@@ -157,6 +161,20 @@ class Epoch(object):
|
||||
else:
|
||||
self.active_for += 1
|
||||
self.inactive_for = 0
|
||||
self.sad_for = 0
|
||||
self.bored_for = 0
|
||||
|
||||
if self.inactive_for >= self.config['personality']['sad_num_epochs']:
|
||||
# sad > bored; cant be sad and bored
|
||||
self.bored_for = 0
|
||||
self.sad_for += 1
|
||||
elif self.inactive_for >= self.config['personality']['bored_num_epochs']:
|
||||
# sad_treshhold > inactive > bored_treshhold; cant be sad and bored
|
||||
self.sad_for = 0
|
||||
self.bored_for += 1
|
||||
else:
|
||||
self.sad_for = 0
|
||||
self.bored_for = 0
|
||||
|
||||
now = time.time()
|
||||
cpu = pwnagotchi.cpu_load()
|
||||
@@ -172,6 +190,8 @@ class Epoch(object):
|
||||
'blind_for_epochs': self.blind_for,
|
||||
'inactive_for_epochs': self.inactive_for,
|
||||
'active_for_epochs': self.active_for,
|
||||
'sad_for_epochs': self.sad_for,
|
||||
'bored_for_epochs': self.bored_for,
|
||||
'missed_interactions': self.num_missed,
|
||||
'num_hops': self.num_hops,
|
||||
'num_peers': self.num_peers,
|
||||
@@ -188,13 +208,15 @@ class Epoch(object):
|
||||
self._epoch_data['reward'] = self._reward(self.epoch + 1, self._epoch_data)
|
||||
self._epoch_data_ready.set()
|
||||
|
||||
logging.info("[epoch %d] duration=%s slept_for=%s blind=%d inactive=%d active=%d peers=%d tot_bond=%.2f "
|
||||
logging.info("[epoch %d] duration=%s slept_for=%s blind=%d sad=%d bored=%d inactive=%d active=%d peers=%d tot_bond=%.2f "
|
||||
"avg_bond=%.2f hops=%d missed=%d deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% "
|
||||
"temperature=%dC reward=%s" % (
|
||||
self.epoch,
|
||||
utils.secs_to_hhmmss(self.epoch_duration),
|
||||
utils.secs_to_hhmmss(self.num_slept),
|
||||
self.blind_for,
|
||||
self.sad_for,
|
||||
self.bored_for,
|
||||
self.inactive_for,
|
||||
self.active_for,
|
||||
self.num_peers,
|
||||
|
@@ -4,31 +4,37 @@ import pwnagotchi.mesh.wifi as wifi
|
||||
|
||||
MAX_EPOCH_DURATION = 1024
|
||||
|
||||
histogram_size = wifi.NumChannels
|
||||
|
||||
shape = (1,
|
||||
# aps per channel
|
||||
histogram_size +
|
||||
# clients per channel
|
||||
histogram_size +
|
||||
# peers per channel
|
||||
histogram_size +
|
||||
# duration
|
||||
1 +
|
||||
# inactive
|
||||
1 +
|
||||
# active
|
||||
1 +
|
||||
# missed
|
||||
1 +
|
||||
# hops
|
||||
1 +
|
||||
# deauths
|
||||
1 +
|
||||
# assocs
|
||||
1 +
|
||||
# handshakes
|
||||
1)
|
||||
def describe(extended=False):
|
||||
if not extended:
|
||||
histogram_size = wifi.NumChannels
|
||||
else:
|
||||
# see https://github.com/evilsocket/pwnagotchi/issues/583
|
||||
histogram_size = wifi.NumChannelsExt
|
||||
|
||||
return histogram_size, (1,
|
||||
# aps per channel
|
||||
histogram_size +
|
||||
# clients per channel
|
||||
histogram_size +
|
||||
# peers per channel
|
||||
histogram_size +
|
||||
# duration
|
||||
1 +
|
||||
# inactive
|
||||
1 +
|
||||
# active
|
||||
1 +
|
||||
# missed
|
||||
1 +
|
||||
# hops
|
||||
1 +
|
||||
# deauths
|
||||
1 +
|
||||
# assocs
|
||||
1 +
|
||||
# handshakes
|
||||
1)
|
||||
|
||||
|
||||
def featurize(state, step):
|
||||
|
@@ -34,10 +34,14 @@ class Environment(gym.Env):
|
||||
self._epoch_num = 0
|
||||
self._last_render = None
|
||||
|
||||
channels = agent.supported_channels()
|
||||
# see https://github.com/evilsocket/pwnagotchi/issues/583
|
||||
self._supported_channels = agent.supported_channels()
|
||||
self._extended_spectrum = any(ch > 140 for ch in self._supported_channels)
|
||||
self._histogram_size, self._observation_shape = featurizer.describe(self._extended_spectrum)
|
||||
|
||||
Environment.params += [
|
||||
Parameter('_channel_%d' % ch, min_value=0, max_value=1, meta=ch + 1) for ch in
|
||||
range(featurizer.histogram_size) if ch + 1 in channels
|
||||
range(self._histogram_size) if ch + 1 in self._supported_channels
|
||||
]
|
||||
|
||||
self.last = {
|
||||
@@ -50,7 +54,7 @@ class Environment(gym.Env):
|
||||
}
|
||||
|
||||
self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable])
|
||||
self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32)
|
||||
self.observation_space = spaces.Box(low=0, high=1, shape=self._observation_shape, dtype=np.float32)
|
||||
self.reward_range = reward.range
|
||||
|
||||
@staticmethod
|
||||
@@ -118,7 +122,7 @@ class Environment(gym.Env):
|
||||
return self.last['state_v']
|
||||
|
||||
def _render_histogram(self, hist):
|
||||
for ch in range(featurizer.histogram_size):
|
||||
for ch in range(self._histogram_size):
|
||||
if hist[ch]:
|
||||
logging.info(" CH %d: %s" % (ch + 1, hist[ch]))
|
||||
|
||||
|
@@ -18,4 +18,10 @@ class RewardFunction(object):
|
||||
m = -.3 * (state['missed_interactions'] / tot_interactions)
|
||||
i = -.2 * (state['inactive_for_epochs'] / tot_epochs)
|
||||
|
||||
return h + a + c + b + i + m
|
||||
# include emotions if state >= 5 epochs
|
||||
_sad = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0
|
||||
_bored = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0
|
||||
s = -.2 * (_sad / tot_epochs)
|
||||
l = -.1 * (_bored / tot_epochs)
|
||||
|
||||
return h + a + c + b + i + m + s + l
|
||||
|
@@ -176,7 +176,7 @@ class AsyncTrainer(object):
|
||||
self.set_training(True, epochs_per_episode)
|
||||
self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step)
|
||||
except Exception as e:
|
||||
logging.exception("[ai] error while training")
|
||||
logging.exception("[ai] error while training (%s)", e)
|
||||
finally:
|
||||
self.set_training(False)
|
||||
obs = self._model.env.reset()
|
||||
|
@@ -12,19 +12,18 @@ class Automata(object):
|
||||
self._epoch = Epoch(config)
|
||||
|
||||
def _on_miss(self, who):
|
||||
logging.info("it looks like %s is not in range anymore :/" % who)
|
||||
logging.info("it looks like %s is not in range anymore :/", who)
|
||||
self._epoch.track(miss=True)
|
||||
self._view.on_miss(who)
|
||||
|
||||
def _on_error(self, who, e):
|
||||
error = "%s" % e
|
||||
# when we're trying to associate or deauth something that is not in range anymore
|
||||
# (if we are moving), we get the following error from bettercap:
|
||||
# error 400: 50:c7:bf:2e:d3:37 is an unknown BSSID or it is in the association skip list.
|
||||
if 'is an unknown BSSID' in error:
|
||||
if 'is an unknown BSSID' in str(e):
|
||||
self._on_miss(who)
|
||||
else:
|
||||
logging.error("%s" % e)
|
||||
logging.error(e)
|
||||
|
||||
def set_starting(self):
|
||||
self._view.on_starting()
|
||||
@@ -58,7 +57,7 @@ class Automata(object):
|
||||
def set_bored(self):
|
||||
factor = self._epoch.inactive_for / self._config['personality']['bored_num_epochs']
|
||||
if not self._has_support_network_for(factor):
|
||||
logging.warning("%d epochs with no activity -> bored" % self._epoch.inactive_for)
|
||||
logging.warning("%d epochs with no activity -> bored", self._epoch.inactive_for)
|
||||
self._view.on_bored()
|
||||
plugins.on('bored', self)
|
||||
else:
|
||||
@@ -68,7 +67,7 @@ class Automata(object):
|
||||
def set_sad(self):
|
||||
factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs']
|
||||
if not self._has_support_network_for(factor):
|
||||
logging.warning("%d epochs with no activity -> sad" % self._epoch.inactive_for)
|
||||
logging.warning("%d epochs with no activity -> sad", self._epoch.inactive_for)
|
||||
self._view.on_sad()
|
||||
plugins.on('sad', self)
|
||||
else:
|
||||
@@ -77,7 +76,7 @@ class Automata(object):
|
||||
|
||||
def set_angry(self, factor):
|
||||
if not self._has_support_network_for(factor):
|
||||
logging.warning("%d epochs with no activity -> angry" % self._epoch.inactive_for)
|
||||
logging.warning("%d epochs with no activity -> angry", self._epoch.inactive_for)
|
||||
self._view.on_angry()
|
||||
plugins.on('angry', self)
|
||||
else:
|
||||
@@ -85,7 +84,7 @@ class Automata(object):
|
||||
self.set_grateful()
|
||||
|
||||
def set_excited(self):
|
||||
logging.warning("%d epochs with activity -> excited" % self._epoch.active_for)
|
||||
logging.warning("%d epochs with activity -> excited", self._epoch.active_for)
|
||||
self._view.on_excited()
|
||||
plugins.on('excited', self)
|
||||
|
||||
@@ -118,17 +117,17 @@ class Automata(object):
|
||||
if factor >= 2.0:
|
||||
self.set_angry(factor)
|
||||
else:
|
||||
logging.warning("agent missed %d interactions -> lonely" % did_miss)
|
||||
logging.warning("agent missed %d interactions -> lonely", did_miss)
|
||||
self.set_lonely()
|
||||
# after X times being bored, the status is set to sad or angry
|
||||
elif self._epoch.inactive_for >= self._config['personality']['sad_num_epochs']:
|
||||
elif self._epoch.sad_for:
|
||||
factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs']
|
||||
if factor >= 2.0:
|
||||
self.set_angry(factor)
|
||||
else:
|
||||
self.set_sad()
|
||||
# after X times being inactive, the status is set to bored
|
||||
elif self._epoch.inactive_for >= self._config['personality']['bored_num_epochs']:
|
||||
elif self._epoch.bored_for:
|
||||
self.set_bored()
|
||||
# after X times being active, the status is set to happy / excited
|
||||
elif self._epoch.active_for >= self._config['personality']['excited_num_epochs']:
|
||||
@@ -139,6 +138,6 @@ class Automata(object):
|
||||
plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data())
|
||||
|
||||
if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']:
|
||||
logging.critical("%d epochs without visible access points -> rebooting ..." % self._epoch.blind_for)
|
||||
logging.critical("%d epochs without visible access points -> rebooting ...", self._epoch.blind_for)
|
||||
self._reboot()
|
||||
self._epoch.blind_for = 0
|
||||
|
@@ -1,5 +1,8 @@
|
||||
import json
|
||||
import logging
|
||||
import requests
|
||||
import websockets
|
||||
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
|
||||
@@ -25,15 +28,27 @@ class Client(object):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.url = "%s://%s:%d/api" % (scheme, hostname, port)
|
||||
self.websocket = "ws://%s:%s@%s:%d/api" % (username, password, hostname, port)
|
||||
self.auth = HTTPBasicAuth(username, password)
|
||||
|
||||
def session(self):
|
||||
r = requests.get("%s/session" % self.url, auth=self.auth)
|
||||
return decode(r)
|
||||
|
||||
def events(self):
|
||||
r = requests.get("%s/events" % self.url, auth=self.auth)
|
||||
return decode(r)
|
||||
async def start_websocket(self, consumer):
|
||||
s = "%s/events" % self.websocket
|
||||
while True:
|
||||
try:
|
||||
async with websockets.connect(s, ping_interval=60, ping_timeout=90) as ws:
|
||||
async for msg in ws:
|
||||
try:
|
||||
await consumer(msg)
|
||||
except Exception as ex:
|
||||
logging.debug("Error while parsing event (%s)", ex)
|
||||
except websockets.exceptions.ConnectionClosedError:
|
||||
logging.debug("Lost websocket connection. Reconnecting...")
|
||||
except websockets.exceptions.WebSocketException as wex:
|
||||
logging.debug("Websocket exception (%s)", wex)
|
||||
|
||||
def run(self, command, verbose_errors=True):
|
||||
r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command})
|
||||
|
238
pwnagotchi/defaults.toml
Normal file
238
pwnagotchi/defaults.toml
Normal file
@@ -0,0 +1,238 @@
|
||||
main.name = ""
|
||||
main.lang = "en"
|
||||
main.confd = "/etc/pwnagotchi/conf.d/"
|
||||
main.custom_plugins = ""
|
||||
main.custom_plugin_repos = [
|
||||
"https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip"
|
||||
]
|
||||
main.iface = "mon0"
|
||||
main.mon_start_cmd = "/usr/bin/monstart"
|
||||
main.mon_stop_cmd = "/usr/bin/monstop"
|
||||
main.mon_max_blind_epochs = 50
|
||||
main.no_restart = false
|
||||
main.whitelist = [
|
||||
"EXAMPLE_NETWORK",
|
||||
"ANOTHER_EXAMPLE_NETWORK",
|
||||
"fo:od:ba:be:fo:od",
|
||||
"fo:od:ba"
|
||||
]
|
||||
main.filter = ""
|
||||
|
||||
main.plugins.grid.enabled = true
|
||||
main.plugins.grid.report = false
|
||||
main.plugins.grid.exclude = [
|
||||
"YourHomeNetworkHere"
|
||||
]
|
||||
|
||||
main.plugins.auto-update.enabled = true
|
||||
main.plugins.auto-update.install = true
|
||||
main.plugins.auto-update.interval = 1
|
||||
|
||||
main.plugins.net-pos.enabled = false
|
||||
main.plugins.net-pos.api_key = "test"
|
||||
|
||||
main.plugins.gps.enabled = false
|
||||
main.plugins.gps.speed = 19200
|
||||
main.plugins.gps.device = "/dev/ttyUSB0"
|
||||
|
||||
main.plugins.webgpsmap.enabled = false
|
||||
|
||||
main.plugins.onlinehashcrack.enabled = false
|
||||
main.plugins.onlinehashcrack.email = ""
|
||||
main.plugins.onlinehashcrack.dashboard = ""
|
||||
main.plugins.onlinehashcrack.single_files = false
|
||||
main.plugins.onlinehashcrack.whitelist = []
|
||||
|
||||
main.plugins.wpa-sec.enabled = false
|
||||
main.plugins.wpa-sec.api_key = ""
|
||||
main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org"
|
||||
main.plugins.wpa-sec.download_results = false
|
||||
main.plugins.wpa-sec.whitelist = []
|
||||
|
||||
main.plugins.wigle.enabled = false
|
||||
main.plugins.wigle.api_key = ""
|
||||
main.plugins.wigle.whitelist = []
|
||||
main.plugins.wigle.donate = true
|
||||
|
||||
main.plugins.bt-tether.enabled = false
|
||||
|
||||
main.plugins.bt-tether.devices.android-phone.enabled = false
|
||||
main.plugins.bt-tether.devices.android-phone.search_order = 1
|
||||
main.plugins.bt-tether.devices.android-phone.mac = ""
|
||||
main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44"
|
||||
main.plugins.bt-tether.devices.android-phone.netmask = 24
|
||||
main.plugins.bt-tether.devices.android-phone.interval = 1
|
||||
main.plugins.bt-tether.devices.android-phone.scantime = 10
|
||||
main.plugins.bt-tether.devices.android-phone.max_tries = 10
|
||||
main.plugins.bt-tether.devices.android-phone.share_internet = false
|
||||
main.plugins.bt-tether.devices.android-phone.priority = 1
|
||||
|
||||
main.plugins.bt-tether.devices.ios-phone.enabled = false
|
||||
main.plugins.bt-tether.devices.ios-phone.search_order = 2
|
||||
main.plugins.bt-tether.devices.ios-phone.mac = ""
|
||||
main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6"
|
||||
main.plugins.bt-tether.devices.ios-phone.netmask = 24
|
||||
main.plugins.bt-tether.devices.ios-phone.interval = 5
|
||||
main.plugins.bt-tether.devices.ios-phone.scantime = 20
|
||||
main.plugins.bt-tether.devices.ios-phone.max_tries = 0
|
||||
main.plugins.bt-tether.devices.ios-phone.share_internet = false
|
||||
main.plugins.bt-tether.devices.ios-phone.priority = 999
|
||||
|
||||
main.plugins.memtemp.enabled = false
|
||||
main.plugins.memtemp.scale = "celsius"
|
||||
main.plugins.memtemp.orientation = "horizontal"
|
||||
|
||||
main.plugins.paw-gps.enabled = false
|
||||
main.plugins.paw-gps.ip = ""
|
||||
|
||||
main.plugins.gpio_buttons.enabled = false
|
||||
|
||||
main.plugins.led.enabled = true
|
||||
main.plugins.led.led = 0
|
||||
main.plugins.led.delay = 200
|
||||
main.plugins.led.patterns.loaded = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.updating = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.unread_inbox = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.ready = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.ai_ready = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.ai_training_start = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.ai_best_reward = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.ai_worst_reward = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.bored = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.sad = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.excited = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.lonely = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.rebooting = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.wait = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.sleep = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.wifi_update = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.association = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.deauthentication = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.handshake = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.epoch = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.peer_detected = "oo oo oo oo oo oo oo"
|
||||
main.plugins.led.patterns.peer_lost = "oo oo oo oo oo oo oo"
|
||||
|
||||
main.plugins.logtail.enabled = false
|
||||
main.plugins.logtail.max-lines = 10000
|
||||
|
||||
main.plugins.session-stats.enabled = true
|
||||
main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/"
|
||||
|
||||
main.log.path = "/var/log/pwnagotchi.log"
|
||||
main.log.rotation.enabled = true
|
||||
main.log.rotation.size = "10M"
|
||||
|
||||
ai.enabled = true
|
||||
ai.path = "/root/brain.nn"
|
||||
ai.laziness = 0.1
|
||||
ai.epochs_per_episode = 50
|
||||
|
||||
ai.params.gamma = 0.99
|
||||
ai.params.n_steps = 1
|
||||
ai.params.vf_coef = 0.25
|
||||
ai.params.ent_coef = 0.01
|
||||
ai.params.max_grad_norm = 0.5
|
||||
ai.params.learning_rate = 0.001
|
||||
ai.params.alpha = 0.99
|
||||
ai.params.epsilon = 0.00001
|
||||
ai.params.verbose = 1
|
||||
ai.params.lr_schedule = "constant"
|
||||
|
||||
personality.advertise = true
|
||||
personality.deauth = true
|
||||
personality.associate = true
|
||||
personality.channels = []
|
||||
personality.min_rssi = -200
|
||||
personality.ap_ttl = 120
|
||||
personality.sta_ttl = 300
|
||||
personality.recon_time = 30
|
||||
personality.max_inactive_scale = 2
|
||||
personality.recon_inactive_multiplier = 2
|
||||
personality.hop_recon_time = 10
|
||||
personality.min_recon_time = 5
|
||||
personality.max_interactions = 3
|
||||
personality.max_misses_for_recon = 5
|
||||
personality.excited_num_epochs = 10
|
||||
personality.bored_num_epochs = 15
|
||||
personality.sad_num_epochs = 25
|
||||
personality.bond_encounters_factor = 20000
|
||||
|
||||
ui.fps = 0.0
|
||||
ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic
|
||||
ui.font.size_offset = 0 # will be added to the font size
|
||||
|
||||
ui.faces.look_r = "( ⚆_⚆)"
|
||||
ui.faces.look_l = "(☉_☉ )"
|
||||
ui.faces.look_r_happy = "( ◕‿◕)"
|
||||
ui.faces.look_l_happy = "(◕‿◕ )"
|
||||
ui.faces.sleep = "(⇀‿‿↼)"
|
||||
ui.faces.sleep2 = "(≖‿‿≖)"
|
||||
ui.faces.awake = "(◕‿‿◕)"
|
||||
ui.faces.bored = "(-__-)"
|
||||
ui.faces.intense = "(°▃▃°)"
|
||||
ui.faces.cool = "(⌐■_■)"
|
||||
ui.faces.happy = "(•‿‿•)"
|
||||
ui.faces.excited = "(ᵔ◡◡ᵔ)"
|
||||
ui.faces.grateful = "(^‿‿^)"
|
||||
ui.faces.motivated = "(☼‿‿☼)"
|
||||
ui.faces.demotivated = "(≖__≖)"
|
||||
ui.faces.smart = "(✜‿‿✜)"
|
||||
ui.faces.lonely = "(ب__ب)"
|
||||
ui.faces.sad = "(╥☁╥ )"
|
||||
ui.faces.angry = "(-_-')"
|
||||
ui.faces.friend = "(♥‿‿♥)"
|
||||
ui.faces.broken = "(☓‿‿☓)"
|
||||
ui.faces.debug = "(#__#)"
|
||||
ui.faces.upload = "(1__0)"
|
||||
ui.faces.upload1 = "(1__1)"
|
||||
ui.faces.upload2 = "(0__1)"
|
||||
|
||||
ui.web.enabled = true
|
||||
ui.web.address = "0.0.0.0"
|
||||
ui.web.username = "changeme"
|
||||
ui.web.password = "changeme"
|
||||
ui.web.origin = ""
|
||||
ui.web.port = 8080
|
||||
ui.web.on_frame = ""
|
||||
|
||||
ui.display.enabled = true
|
||||
ui.display.rotation = 180
|
||||
ui.display.type = "waveshare_2"
|
||||
ui.display.color = "black"
|
||||
|
||||
bettercap.scheme = "http"
|
||||
bettercap.hostname = "localhost"
|
||||
bettercap.port = 8081
|
||||
bettercap.username = "pwnagotchi"
|
||||
bettercap.password = "pwnagotchi"
|
||||
bettercap.handshakes = "/root/handshakes"
|
||||
bettercap.silence = [
|
||||
"ble.device.new",
|
||||
"ble.device.lost",
|
||||
"ble.device.disconnected",
|
||||
"ble.device.connected",
|
||||
"ble.device.service.discovered",
|
||||
"ble.device.characteristic.discovered",
|
||||
"wifi.client.new",
|
||||
"wifi.client.lost",
|
||||
"wifi.client.probe",
|
||||
"wifi.ap.new",
|
||||
"wifi.ap.lost",
|
||||
"mod.started"
|
||||
]
|
||||
|
||||
fs.memory.enabled = false
|
||||
fs.memory.mounts.log.enabled = false
|
||||
fs.memory.mounts.log.mount = "/var/log"
|
||||
fs.memory.mounts.log.size = "50M"
|
||||
fs.memory.mounts.log.sync = 60
|
||||
fs.memory.mounts.log.zram = true
|
||||
fs.memory.mounts.log.rsync = true
|
||||
|
||||
fs.memory.mounts.data.enabled = false
|
||||
fs.memory.mounts.data.mount = "/var/tmp/pwnagotchi"
|
||||
fs.memory.mounts.data.size = "10M"
|
||||
fs.memory.mounts.data.sync = 3600
|
||||
fs.memory.mounts.data.zram = false
|
||||
fs.memory.mounts.data.rsync = true
|
@@ -1,276 +0,0 @@
|
||||
# WARNING WARNING WARNING WARNING
|
||||
#
|
||||
# This file is recreated with default settings on every pwnagotchi restart,
|
||||
# use /etc/pwnagotchi/config.yml to configure this unit.
|
||||
#
|
||||
#
|
||||
# main algorithm configuration
|
||||
main:
|
||||
# if set this will set the hostname of the unit. min length is 2, max length 25, only a-zA-Z0-9- allowed
|
||||
name: ''
|
||||
# currently implemented: en (default), de, el, fr, it, mk, nl, ru, se, pt-BR, es, pt
|
||||
lang: en
|
||||
# custom plugins path, if null only default plugins with be loaded
|
||||
custom_plugins:
|
||||
# which plugins to load and enable
|
||||
plugins:
|
||||
grid:
|
||||
enabled: true
|
||||
report: false # don't report pwned networks by default!
|
||||
exclude: # do not report the following networks (accepts both ESSIDs and BSSIDs)
|
||||
- YourHomeNetworkHere
|
||||
|
||||
auto-update:
|
||||
enabled: true
|
||||
install: true # if false, it will only warn that updates are available, if true it will install them
|
||||
interval: 1 # every 1 hour
|
||||
|
||||
auto-backup:
|
||||
enabled: false
|
||||
interval: 1 # every day
|
||||
max_tries: 0 # 0=infinity
|
||||
files:
|
||||
- /root/brain.nn
|
||||
- /root/brain.json
|
||||
- /root/.api-report.json
|
||||
- /root/handshakes/
|
||||
- /root/peers/
|
||||
- /etc/pwnagotchi/
|
||||
- /var/log/pwnagotchi.log
|
||||
commands:
|
||||
- 'tar czf /root/pwnagotchi-backup.tar.gz {files}'
|
||||
net-pos:
|
||||
enabled: false
|
||||
api_key: 'test'
|
||||
gps:
|
||||
enabled: false
|
||||
speed: 19200
|
||||
device: /dev/ttyUSB0
|
||||
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: ~
|
||||
api_url: "https://wpa-sec.stanev.org"
|
||||
wigle:
|
||||
enabled: false
|
||||
api_key: ~
|
||||
screen_refresh:
|
||||
enabled: false
|
||||
refresh_interval: 50
|
||||
quickdic:
|
||||
enabled: false
|
||||
wordlist_folder: /opt/wordlists/
|
||||
AircrackOnly:
|
||||
enabled: false
|
||||
bt-tether:
|
||||
enabled: false # if you want to use this, set ui.display.video.address to 0.0.0.0
|
||||
devices:
|
||||
android-phone:
|
||||
enabled: false
|
||||
search_order: 1 # search for this first
|
||||
mac: ~ # mac of your bluetooth device
|
||||
ip: '192.168.44.44' # ip from which your pwnagotchi should be reachable
|
||||
netmask: 24
|
||||
interval: 1 # check every minute for device
|
||||
scantime: 10 # search for 10 seconds
|
||||
max_tries: 10 # after 10 tries of "not found"; don't try anymore
|
||||
share_internet: false
|
||||
priority: 1 # low routing priority; ios (prio: 999) would win here
|
||||
ios-phone:
|
||||
enabled: false
|
||||
search_order: 2 # search for this second
|
||||
mac: ~ # mac of your bluetooth device
|
||||
ip: '172.20.10.6' # ip from which your pwnagotchi should be reachable
|
||||
netmask: 24
|
||||
interval: 5 # check every 5 minutes for device
|
||||
scantime: 20
|
||||
max_tries: 0 # infinity
|
||||
share_internet: false
|
||||
priority: 999 # routing priority
|
||||
memtemp: # Display memory usage, cpu load and cpu temperature on screen
|
||||
enabled: false
|
||||
orientation: horizontal # horizontal/vertical
|
||||
pawgps:
|
||||
enabled: false
|
||||
#The IP Address of your phone with Paw Server running, default (option is empty) is 192.168.44.1
|
||||
ip: ''
|
||||
gpio_buttons:
|
||||
enabled: false
|
||||
#The following is a list of the GPIO number for your button, and the command you want to run when it is pressed
|
||||
gpios:
|
||||
- 20: 'sudo touch /root/.pwnagotchi-auto && sudo systemctl restart pwnagotchi'
|
||||
- 21: 'shutdown -h now'
|
||||
# monitor interface to use
|
||||
iface: mon0
|
||||
# command to run to bring the mon interface up in case it's not up already
|
||||
mon_start_cmd: /usr/bin/monstart
|
||||
mon_stop_cmd: /usr/bin/monstop
|
||||
mon_max_blind_epochs: 50
|
||||
# log file
|
||||
log: /var/log/pwnagotchi.log
|
||||
# if true, will not restart the wifi module
|
||||
no_restart: false
|
||||
# access points to ignore
|
||||
whitelist:
|
||||
- EXAMPLE_NETWORK
|
||||
- ANOTHER_EXAMPLE_NETWORK
|
||||
# if not null, filter access points by this regular expression
|
||||
filter: null
|
||||
|
||||
ai:
|
||||
# if false, only the default 'personality' will be used
|
||||
enabled: true
|
||||
path: /root/brain.nn
|
||||
# 1.0 - laziness = probability of start training
|
||||
laziness: 0.1
|
||||
# how many epochs to train on
|
||||
epochs_per_episode: 50
|
||||
params:
|
||||
# discount factor
|
||||
gamma: 0.99
|
||||
# the number of steps to run for each environment per update
|
||||
n_steps: 1
|
||||
# value function coefficient for the loss calculation
|
||||
vf_coef: 0.25
|
||||
# entropy coefficient for the loss calculation
|
||||
ent_coef: 0.01
|
||||
# maximum value for the gradient clipping
|
||||
max_grad_norm: 0.5
|
||||
# the learning rate
|
||||
learning_rate: 0.0010
|
||||
# rmsprop decay parameter
|
||||
alpha: 0.99
|
||||
# rmsprop epsilon
|
||||
epsilon: 0.00001
|
||||
# the verbosity level: 0 none, 1 training information, 2 tensorflow debug
|
||||
verbose: 1
|
||||
# type of scheduler for the learning rate update ('linear', 'constant', 'double_linear_con', 'middle_drop' or 'double_middle_drop')
|
||||
lr_schedule: 'constant'
|
||||
# the log location for tensorboard (if None, no logging)
|
||||
tensorboard_log: null
|
||||
|
||||
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
|
||||
# number of encounters (times met on a channel) with another unit before considering it a friend and bond
|
||||
# also used for cumulative bonding score of nearby units
|
||||
bond_encounters_factor: 20000
|
||||
|
||||
# ui configuration
|
||||
ui:
|
||||
# here you can customize the faces
|
||||
faces:
|
||||
look_r: '( ⚆_⚆)'
|
||||
look_l: '(☉_☉ )'
|
||||
look_r_happy: '( ◕‿◕)'
|
||||
look_l_happy: '(◕‿◕ )'
|
||||
sleep: '(⇀‿‿↼)'
|
||||
sleep2: '(≖‿‿≖)'
|
||||
awake: '(◕‿‿◕)'
|
||||
bored: '(-__-)'
|
||||
intense: '(°▃▃°)'
|
||||
cool: '(⌐■_■)'
|
||||
happy: '(•‿‿•)'
|
||||
excited: '(ᵔ◡◡ᵔ)'
|
||||
grateful: '(^‿‿^)'
|
||||
motivated: '(☼‿‿☼)'
|
||||
demotivated: '(≖__≖)'
|
||||
smart: '(✜‿‿✜)'
|
||||
lonely: '(ب__ب)'
|
||||
sad: '(╥☁╥ )'
|
||||
angry: "(-_-')"
|
||||
friend: '(♥‿‿♥)'
|
||||
broken: '(☓‿‿☓)'
|
||||
debug: '(#__#)'
|
||||
# ePaper display can update every 3 secs anyway, set to 0 to only refresh for major data changes
|
||||
# IMPORTANT: The lifespan of an eINK display depends on the cumulative amount of refreshes. If you want to
|
||||
# preserve your display over time, you should set this value to 0.0 so that the display will be refreshed only
|
||||
# if any of the important data fields changed (the uptime and blinking cursor won't trigger a refresh).
|
||||
fps: 0.0
|
||||
display:
|
||||
enabled: true
|
||||
rotation: 180
|
||||
# Possible options inkyphat/inky, papirus/papi, waveshare_1/ws_1 or waveshare_2/ws_2, oledhat, lcdhat, waveshare154inch, waveshare27inch, dfrobot/df
|
||||
type: 'waveshare_2'
|
||||
# Possible options red/yellow/black (black used for monocromatic displays)
|
||||
# Waveshare tri-color 2.13in display can be over-driven with color set as 'fastAndFurious'
|
||||
# THIS IS POTENTIALLY DANGEROUS. DO NOT USE UNLESS YOU UNDERSTAND THAT IT COULD KILL YOUR DISPLAY
|
||||
color: 'black'
|
||||
video:
|
||||
enabled: true
|
||||
address: '0.0.0.0'
|
||||
origin: null
|
||||
port: 8080
|
||||
# command to be executed when a new png frame is available
|
||||
# for instance, to use with framebuffer based displays:
|
||||
# on_frame: 'fbi --noverbose -a -d /dev/fb1 -T 1 /root/pwnagotchi.png > /dev/null 2>&1'
|
||||
on_frame: ''
|
||||
|
||||
|
||||
# bettercap rest api configuration
|
||||
bettercap:
|
||||
# api scheme://hostname:port username and password
|
||||
scheme: http
|
||||
hostname: localhost
|
||||
port: 8081
|
||||
username: pwnagotchi
|
||||
password: pwnagotchi
|
||||
# folder where bettercap stores the WPA handshakes, given that
|
||||
# wifi.handshakes.aggregate will be set to false and individual
|
||||
# pcap files will be created in order to minimize the chances
|
||||
# of a single pcap file to get corrupted
|
||||
handshakes: /root/handshakes
|
||||
# events to mute in bettercap's events stream
|
||||
silence:
|
||||
- ble.device.new
|
||||
- ble.device.lost
|
||||
- ble.device.disconnected
|
||||
- ble.device.connected
|
||||
- ble.device.service.discovered
|
||||
- ble.device.characteristic.discovered
|
||||
- wifi.client.new
|
||||
- wifi.client.lost
|
||||
- wifi.client.probe
|
||||
- wifi.ap.new
|
||||
- wifi.ap.lost
|
||||
- mod.started
|
190
pwnagotchi/fs/__init__.py
Normal file
190
pwnagotchi/fs/__init__.py
Normal file
@@ -0,0 +1,190 @@
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import contextlib
|
||||
import shutil
|
||||
import _thread
|
||||
import logging
|
||||
|
||||
from time import sleep
|
||||
from distutils.dir_util import copy_tree
|
||||
|
||||
mounts = list()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def ensure_write(filename, mode='w'):
|
||||
path = os.path.dirname(filename)
|
||||
fd, tmp = tempfile.mkstemp(dir=path)
|
||||
|
||||
with os.fdopen(fd, mode) as f:
|
||||
yield f
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
|
||||
os.replace(tmp, filename)
|
||||
|
||||
|
||||
def size_of(path):
|
||||
"""
|
||||
Calculate the sum of all the files in path
|
||||
"""
|
||||
total = 0
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
total += os.path.getsize(os.path.join(root, f))
|
||||
return total
|
||||
|
||||
|
||||
def is_mountpoint(path):
|
||||
"""
|
||||
Checks if path is mountpoint
|
||||
"""
|
||||
return os.system(f"mountpoint -q {path}") == 0
|
||||
|
||||
|
||||
def setup_mounts(config):
|
||||
"""
|
||||
Sets up all the configured mountpoints
|
||||
"""
|
||||
global mounts
|
||||
fs_cfg = config['fs']['memory']
|
||||
if not fs_cfg['enabled']:
|
||||
return
|
||||
|
||||
for name, options in fs_cfg['mounts'].items():
|
||||
if not options['enabled']:
|
||||
continue
|
||||
logging.debug("[FS] Trying to setup mount %s (%s)", name, options['mount'])
|
||||
size,unit = re.match(r"(\d+)([a-zA-Z]+)", options['size']).groups()
|
||||
target = os.path.join('/run/pwnagotchi/disk/', os.path.basename(options['mount']))
|
||||
|
||||
is_mounted = is_mountpoint(target)
|
||||
logging.debug("[FS] %s is %s mounted", options['mount'],
|
||||
"already" if is_mounted else "not yet")
|
||||
|
||||
m = MemoryFS(
|
||||
options['mount'],
|
||||
target,
|
||||
size=options['size'],
|
||||
zram=options['zram'],
|
||||
zram_disk_size=f"{int(size)*2}{unit}",
|
||||
rsync=options['rsync'])
|
||||
|
||||
if not is_mounted:
|
||||
if not m.mount():
|
||||
logging.debug(f"Error while mounting {m.mountpoint}")
|
||||
continue
|
||||
|
||||
if not m.sync(to_ram=True):
|
||||
logging.debug(f"Error while syncing to {m.mountpoint}")
|
||||
m.umount()
|
||||
continue
|
||||
|
||||
interval = int(options['sync'])
|
||||
if interval:
|
||||
logging.debug("[FS] Starting thread to sync %s (interval: %d)",
|
||||
options['mount'], interval)
|
||||
_thread.start_new_thread(m.daemonize, (interval,))
|
||||
else:
|
||||
logging.debug("[FS] Not syncing %s, because interval is 0",
|
||||
options['mount'])
|
||||
|
||||
mounts.append(m)
|
||||
|
||||
|
||||
class MemoryFS:
|
||||
@staticmethod
|
||||
def zram_install():
|
||||
if not os.path.exists("/sys/class/zram-control"):
|
||||
logging.debug("[FS] Installing zram")
|
||||
return os.system("modprobe zram") == 0
|
||||
return True
|
||||
|
||||
|
||||
@staticmethod
|
||||
def zram_dev():
|
||||
logging.debug("[FS] Adding zram device")
|
||||
return open("/sys/class/zram-control/hot_add", "rt").read().strip("\n")
|
||||
|
||||
|
||||
def __init__(self, mount, disk, size="40M",
|
||||
zram=True, zram_alg="lz4", zram_disk_size="100M",
|
||||
zram_fs_type="ext4", rsync=True):
|
||||
self.mountpoint = mount
|
||||
self.disk = disk
|
||||
self.size = size
|
||||
self.zram = zram
|
||||
self.zram_alg = zram_alg
|
||||
self.zram_disk_size = zram_disk_size
|
||||
self.zram_fs_type = zram_fs_type
|
||||
self.zdev = None
|
||||
self.rsync = True
|
||||
self._setup()
|
||||
|
||||
|
||||
def _setup(self):
|
||||
if self.zram and MemoryFS.zram_install():
|
||||
# setup zram
|
||||
self.zdev = MemoryFS.zram_dev()
|
||||
open(f"/sys/block/zram{self.zdev}/comp_algorithm", "wt").write(self.zram_alg)
|
||||
open(f"/sys/block/zram{self.zdev}/disksize", "wt").write(self.zram_disk_size)
|
||||
open(f"/sys/block/zram{self.zdev}/mem_limit", "wt").write(self.size)
|
||||
logging.debug("[FS] Creating fs (type: %s)", self.zram_fs_type)
|
||||
os.system(f"mke2fs -t {self.zram_fs_type} /dev/zram{self.zdev} >/dev/null 2>&1")
|
||||
|
||||
# ensure mountpoints exist
|
||||
if not os.path.exists(self.disk):
|
||||
logging.debug("[FS] Creating %s", self.disk)
|
||||
os.makedirs(self.disk)
|
||||
|
||||
if not os.path.exists(self.mountpoint):
|
||||
logging.debug("[FS] Creating %s", self.mountpoint)
|
||||
os.makedirs(self.mountpoint)
|
||||
|
||||
|
||||
def daemonize(self, interval=60):
|
||||
logging.debug("[FS] Daemonized...")
|
||||
while True:
|
||||
self.sync()
|
||||
sleep(interval)
|
||||
|
||||
|
||||
def sync(self, to_ram=False):
|
||||
source, dest = (self.disk, self.mountpoint) if to_ram else (self.mountpoint, self.disk)
|
||||
needed, actually_free = size_of(source), shutil.disk_usage(dest)[2]
|
||||
if actually_free >= needed:
|
||||
logging.debug("[FS] Syncing %s -> %s", source,dest)
|
||||
if self.rsync:
|
||||
os.system(f"rsync -aXv --inplace --no-whole-file --delete-after {source}/ {dest}/ >/dev/null 2>&1")
|
||||
else:
|
||||
copy_tree(source, dest, preserve_symlinks=True)
|
||||
os.system("sync")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def mount(self):
|
||||
if os.system(f"mount --bind {self.mountpoint} {self.disk}"):
|
||||
return False
|
||||
|
||||
if os.system(f"mount --make-private {self.disk}"):
|
||||
return False
|
||||
|
||||
if self.zram and self.zdev is not None:
|
||||
if os.system(f"mount -t {self.zram_fs_type} -o nosuid,noexec,nodev,user=pwnagotchi /dev/zram{self.zdev} {self.mountpoint}/"):
|
||||
return False
|
||||
else:
|
||||
if os.system(f"mount -t tmpfs -o nosuid,noexec,nodev,mode=0755,size={self.size} pwnagotchi {self.mountpoint}/"):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def umount(self):
|
||||
if os.system(f"umount -l {self.mountpoint}"):
|
||||
return False
|
||||
|
||||
if os.system(f"umount -l {self.disk}"):
|
||||
return False
|
||||
return True
|
@@ -12,9 +12,13 @@ API_ADDRESS = "http://127.0.0.1:8666/api/v1"
|
||||
|
||||
def is_connected():
|
||||
try:
|
||||
socket.create_connection(("www.google.com", 80))
|
||||
return True
|
||||
except OSError:
|
||||
# check DNS
|
||||
host = socket.gethostbyname('api.pwnagotchi.ai')
|
||||
if host:
|
||||
# check connectivity itself
|
||||
socket.create_connection((host, 443), timeout=30)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
@@ -22,9 +26,11 @@ def is_connected():
|
||||
def call(path, obj=None):
|
||||
url = '%s%s' % (API_ADDRESS, path)
|
||||
if obj is None:
|
||||
r = requests.get(url, headers=None)
|
||||
r = requests.get(url, headers=None, timeout=(30.0, 60.0))
|
||||
elif isinstance(obj, dict):
|
||||
r = requests.post(url, headers=None, json=obj, timeout=(30.0, 60.0))
|
||||
else:
|
||||
r = requests.post(url, headers=None, json=obj)
|
||||
r = requests.post(url, headers=None, data=obj, timeout=(30.0, 60.0))
|
||||
|
||||
if r.status_code != 200:
|
||||
raise Exception("(status %d) %s" % (r.status_code, r.text))
|
||||
@@ -39,6 +45,14 @@ def set_advertisement_data(data):
|
||||
return call("/mesh/data", obj=data)
|
||||
|
||||
|
||||
def get_advertisement_data():
|
||||
return call("/mesh/data")
|
||||
|
||||
|
||||
def memory():
|
||||
return call("/mesh/memory")
|
||||
|
||||
|
||||
def peers():
|
||||
return call("/mesh/peers")
|
||||
|
||||
@@ -71,7 +85,7 @@ def update_data(last_session):
|
||||
},
|
||||
'uname': subprocess.getoutput("uname -a"),
|
||||
'brain': brain,
|
||||
'version': pwnagotchi.version
|
||||
'version': pwnagotchi.__version__
|
||||
}
|
||||
|
||||
logging.debug("updating grid data: %s" % data)
|
||||
@@ -95,3 +109,15 @@ def report_ap(essid, bssid):
|
||||
def inbox(page=1, with_pager=False):
|
||||
obj = call("/inbox?p=%d" % page)
|
||||
return obj["messages"] if not with_pager else obj
|
||||
|
||||
|
||||
def inbox_message(id):
|
||||
return call("/inbox/%d" % int(id))
|
||||
|
||||
|
||||
def mark_message(id, mark):
|
||||
return call("/inbox/%d/%s" % (int(id), str(mark)))
|
||||
|
||||
|
||||
def send_message(to, message):
|
||||
return call("/unit/%s/inbox" % to, message.encode('utf-8'))
|
||||
|
BIN
pwnagotchi/locale/af/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/af/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
248
pwnagotchi/locale/af/LC_MESSAGES/voice.po
Normal file
248
pwnagotchi/locale/af/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,248 @@
|
||||
# Afrikaans translation of pwnagotchi.
|
||||
# Copyright (C) 2020.
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# FIRST AUTHOR MatthewNunu https://github.com/MatthewNunu, 2020.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.5.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: MatthewNunu https://github.com/MatthewNunu\n"
|
||||
"Language-Team: \n"
|
||||
"Language: Afrikaans\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hi, ek is Pwnagotchi! Aanvang ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Nuwe dag, nuwe jag, nuwe pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack die wêreld!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI gereed."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Die neurale netwerk is gereed."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Genereer wagwoord, moenie afskakel nie ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Haai, kanaal {channel} is gratis! Jou AP sal dankie sê."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Lees laaste sessie logs ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Ek het {lines_so_far} tot dusver gelees ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Ek's verveeld ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Kom ons gaan vir 'n loopie!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Dit is die beste dag van my lewe!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Poes kak dag :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Ek's baie verveeld ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Ek's baie hartseer ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Ek's hartseer ..."
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Los my uit ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Ek is kwaad vir jou!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Ek leef die lewe!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Ek pwn daarom is ek."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Soveel netwerke!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Ek het soveel pret!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "My misdaad is dié van nuuskierigheid ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hallo {name}! Lekker om jou te ontmoet."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Yo {name}! Sup?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Haai {name} hoe doen jy?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Eenheid {name}} is naby!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... totsiens {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} is weg ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Whoops ... {name} is weg."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} gemis!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Gemis!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Goeie vriende is 'n seën!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Ek is lief vir my vriende!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Niemand wil met my speel nie ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Ek voel so alleen ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Waar is almal?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Slaap vir {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Goeie nag."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Wag tans vir {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Rondkyk ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Haai {what} kom ons wees vriende!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Assosieer na {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Yo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Net besluit dat {mac} geen WiFi nodig het nie!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Deauthenticating {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Kickbanning {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Koel, ons het {num} nuwe handdruk gekry!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Jy het {count} nuwe boodskap!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, iets het verkeerd gegaan ... Herlaai ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Geskop {num} stasies\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Gemaak {num} nuwe vriende\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Het {num} handdrukke\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Ontmoet 1 eweknie"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Ontmoet {num} eweknie"
|
||||
|
||||
#, 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 ""
|
||||
"Ek was pwning vir {duration} en het {deauthed} kliënte geskop! Ek het ook ontmoet "
|
||||
"{associated} nuwe vriende en het {handshakes} handdrukke geëet! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "uur"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minute"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "sekondes"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "uur"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuut"
|
||||
|
||||
msgid "second"
|
||||
msgstr "tweede"
|
Binary file not shown.
@@ -177,7 +177,7 @@ msgstr "Супер, имаме {num} нови handshake{plural}!"
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Имате {count} нови съобщения!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Упс, нещо се обърка ... Рестартиране ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
Binary file not shown.
@@ -40,7 +40,7 @@ msgstr "创建密钥中, 请勿断电..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr ""
|
||||
msgstr "嘿,频道{channel}是免费的!你的AP会说谢谢。"
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "我无聊了..."
|
||||
@@ -84,7 +84,7 @@ msgstr "你好{name}!很高兴认识你."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr ""
|
||||
msgstr "小队{name}就在附近!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
@@ -177,7 +177,7 @@ msgstr "太酷了, 我们抓到了{num}新的猎物{plural}!"
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "主人,有{count}新消息{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "行动,额等等有点小问题... 重启ing ..."
|
||||
|
||||
#, python-brace-format
|
||||
@@ -193,7 +193,7 @@ msgid "Got {num} handshakes\n"
|
||||
msgstr "捕获了{num}握手包\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr ""
|
||||
msgstr "有{num}同龄人"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
|
BIN
pwnagotchi/locale/cs/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/cs/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
249
pwnagotchi/locale/cs/LC_MESSAGES/voice.po
Normal file
249
pwnagotchi/locale/cs/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,249 @@
|
||||
# pwnigotchi voice data
|
||||
# Copyright (C) 2020
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# FIRST AUTHOR czechball@users.noreply.github.com, 2020.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-14 06:15+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Czechball <czechball@users.noreply.github.com>\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Ahoj, já jsem Pwnagotchi! Startuju ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Nový den, nový lov, nové úlovky!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackni celou planetu!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI připraveno."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Neuronová síť je připravena."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generování klíčů, nevypínej mě..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hej, kanál {channel} je volný! Tvůj AP ti poděkuje."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Čtení posledních zpráv z logu ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Zatím přečteno {lines_so_far} řádků logu ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Nudím se ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Pojďme se projít!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Tohle je nejlepší den mého života!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Na hovno den :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Strašně se nudím ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Jsem dost smutný ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Jsem smutný"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Nech mě být ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Jsem na tebe naštvaný!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Tohle je život!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Chytám pakety a tedy jsem."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Tolik sítí!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Tohle je super zábava!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Jsem kriminálně zvědavý ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Ahoj {name}! Rád tě poznávám."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Hej {name}! Jak to jde?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Ahoj {name}, jak se máš?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Jednotka {name} je nablízku!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm... Měj se {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} je pryč ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Whoops ... {name} je pryč."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "Chybí mi {name}!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Chybíš mi!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Dobří kamarádi jsou požehnání!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Miluju svoje kamarády!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Nikdo si se mnou nechce hrát ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Cítím se tak osamělý ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Kde jsou všichni?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Spím {secs} sekund ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Dobrou noc."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Čekání {secs} sekund ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Rozhlížím se ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hej {what} budeme kamarádi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Asociuju se s {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Čus {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Rozhodl jsem se, že {mac} nepotřebuje žádnou WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Deautentikuju {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Kickbanuju {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Super, máme {num} nových handshaků!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Máš {count} nových zpráv!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ups, něco se pokazilo ... Restartuju ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Vykopnuto {num} klientů\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Mám {num} nových kamarádů\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Mám {num} handshaků\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Potkal jsem jednoho kámoše"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Potkal jsem {num} kámošů"
|
||||
|
||||
#, 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 ""
|
||||
"Chytal jsem pakety po dobu {duration} a vykopnul jsem {deauthed} klientů! Taky jsem potkal "
|
||||
"{associated} nových kamarádů a snědl {handshakes} handshaků! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "hodiny"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minuty"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "sekundy"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hodina"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuta"
|
||||
|
||||
msgid "second"
|
||||
msgstr "sekunda"
|
Binary file not shown.
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-23 20:56+0200\n"
|
||||
"POT-Creation-Date: 2019-11-14 21:15+0100\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"
|
||||
@@ -20,13 +20,13 @@ msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hi, ich bin ein Pwnagotchi! Starte ..."
|
||||
msgstr "Hi, ich bin ein Pwnagotchi! Starte..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Neuer Tag, neue Jagd, neue Pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack den Planet!"
|
||||
msgstr "Hack den Planeten!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "KI bereit."
|
||||
@@ -35,23 +35,30 @@ msgid "The neural network is ready."
|
||||
msgstr "Das neurale Netz ist bereit."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generiere Keys, nicht ausschalten ..."
|
||||
msgstr "Generiere Schlüssel, nicht ausschalten..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hey, Channel {channel} ist frei! Dein AP wir des dir danken."
|
||||
msgstr "Hey, Channel {channel} ist frei! Dein AP wird es Dir danken."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Lese die Logs der letzten Session..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Bisher {lines_so_far} Zeilen im Log gelesen..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Mir ist langweilig..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Lass uns laufen gehen!"
|
||||
msgstr "Lass uns spazieren gehen!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Das ist der beste Tag meines Lebens."
|
||||
msgstr "Das ist der beste Tag meines Lebens!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Scheis Tag :/"
|
||||
msgstr "Scheißtag :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Mir ist sau langweilig..."
|
||||
@@ -62,6 +69,13 @@ msgstr "Ich bin sehr traurig..."
|
||||
msgid "I'm sad"
|
||||
msgstr "Ich bin traurig"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Lass mich in Ruhe..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Ich bin sauer auf Dich!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Ich lebe das Leben!"
|
||||
|
||||
@@ -69,33 +83,41 @@ msgid "I pwn therefore I am."
|
||||
msgstr "Ich pwne, also bin ich."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "So viele Netwerke!!!"
|
||||
msgstr "So viele Netzwerke!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Ich habe sooo viel Spaß!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Mein Verbrechen ist das der Neugier ..."
|
||||
msgstr "Mein Verbrechen ist das der Neugier..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hallo {name}, nett Dich kennenzulernen."
|
||||
msgstr "Hallo {name}, schön Dich kennenzulernen."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Jo {name}! Was geht!?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Hey {name}, wie geht's?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Gerät {name} ist in der nähe!!"
|
||||
msgstr "Gerät {name} ist in der Nähe!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ...tschüß {name}"
|
||||
msgstr "Uhm... tschüß {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} ist weg ..."
|
||||
msgstr "{name} ist weg..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Whoops ...{name} ist weg."
|
||||
msgstr "Whoops... {name} ist weg."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
@@ -111,17 +133,17 @@ msgid "I love my friends!"
|
||||
msgstr "Ich liebe meine Freunde!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Niemand will mit mir spielen ..."
|
||||
msgstr "Niemand will mit mir spielen..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Ich fühl michso alleine ..."
|
||||
msgstr "Ich fühl' mich so allein..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Wo sind denn alle?"
|
||||
msgstr "Wo sind denn alle?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Schlafe für {secs}s"
|
||||
msgstr "Schlafe für {secs}s..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr ""
|
||||
@@ -138,7 +160,7 @@ msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Warte für {secs}s ..."
|
||||
msgstr "Warte für {secs}s..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
@@ -158,7 +180,7 @@ msgstr "Jo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Ich denke, dass {mac} kein WiFi brauch!"
|
||||
msgstr "Ich denke, dass {mac} kein WiFi braucht!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
@@ -176,16 +198,16 @@ msgstr "Cool, wir haben {num} neue Handshake{plural}!"
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Cool, wir haben {num} neue Handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, da ist etwas schief gelaufen ...Starte neu ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, da ist was schief gelaufen... Starte neu..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "{num} Stationen gekicked\n"
|
||||
msgstr "{num} Stationen gekickt\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "{num} Freunde gefunden\n"
|
||||
msgstr "{num} neue Freunde gefunden\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
@@ -225,3 +247,4 @@ msgstr "Minute"
|
||||
|
||||
msgid "second"
|
||||
msgstr "Sekunde"
|
||||
|
||||
|
BIN
pwnagotchi/locale/dk/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/dk/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
248
pwnagotchi/locale/dk/LC_MESSAGES/voice.po
Normal file
248
pwnagotchi/locale/dk/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,248 @@
|
||||
# pwnagotchi danish voice data
|
||||
# Copyright (C) 2020
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# Dennis Kjær Jensen <signout@signout.dk>, 2020
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
|
||||
"PO-Revision-Date: 2020-01-18 21:56+ZONE\n"
|
||||
"Last-Translator: Dennis Kjær Jensen <signout@signout.dk>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: Danish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hej. Jeg er Pwnagotchi. Starter ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Ny dag, ny jagt, nye pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack planeten!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI klar."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Det neurale netværk er klart."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Genererer nøgler, sluk ikke ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hey, kanal {channel} er ubrugt! Dit AP vil takke dig."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Læser seneste session logs ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Har læst {lines_so_far} linjer indtil nu ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Jeg keder mig ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Lad os gå en tur!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Det er den bedste dag i mit liv!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Elendig dag :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Jeg keder mig ekstremt meget ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Jeg er meget trist ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Jeg er trist"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Lad mig være i fred"
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Jeg er sur på dig!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Jeg lever livet!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Jeg pwner, derfor er jeg."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Så mange netværk!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Jeg har det vildt sjovt!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Min forbrydelse er at være nysgerrig ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hej {name}! Rart at møde dig."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Hey {name}! Hvasså?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Hej {name} hvordan har du det?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Enheden {name} er lige i nærheden!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... farvel {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} er væk ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Hovsa ... {name} er væk."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} glippede!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Fordømt!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Gode venner en velsignelse!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Jeg elsker mine venner!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Der er ingen der vil lege med mig ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Jeg føler mig så alene ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Hvor er alle henne?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Sover i {secs} sekunder"
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz {secs} sekunder"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Godnat."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Venter i {secs} sekunder"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Kigger mig omkring i {secs} sekunder"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hej {what} lad os være venner!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Associerer til {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Hey {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Besluttede at {mac} ikke har brug for WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Afmelder {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Kickbanner {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Fedt, vi har fået {num} nye handshake{plural}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Du har {count} nye beskeder"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ups, noget gik galt ... Genstarter."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Sparkede {num} af\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Har fået {num} nye venner\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Har fået {num} nyehandshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Har mødt 1 peer"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Har mødt {num} peers"
|
||||
|
||||
#, 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 "Jeg har pwnet i {duration} og kicket {dauthed} klienter! Jeg har også "
|
||||
"mødt {associated} nye venner og spist {handshakes} håndtryk! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "timer"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutter"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "sekunder"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "time"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minut"
|
||||
|
||||
msgid "second"
|
||||
msgstr "sekund"
|
Binary file not shown.
@@ -158,7 +158,7 @@ msgstr "Μπανάρω την {mac}!"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Τέλεια δικέ μου, πήραμε {num} νέες χειραψίες!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ουπς, κάτιπήγε λάθος ... Επανεκκινούμαι ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
Binary file not shown.
@@ -8,25 +8,26 @@ msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-09 17:42+0200\n"
|
||||
"PO-Revision-Date: 2019-10-09 21:07+0000\n"
|
||||
"Last-Translator: diegopastor <dpastor29@alumnos.uaq.mx>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: spanish\n"
|
||||
"PO-Revision-Date: 2020-08-25 23:06+0200\n"
|
||||
"Last-Translator: Sergio Ruiz <serginator@gmail.com>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language-Team: \n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hola, soy Pwnagotchi! Empezando ..."
|
||||
msgstr "¡Hola, soy Pwnagotchi! Empezando ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Nuevo día, nueva cazería, nuevos pwns!"
|
||||
msgstr "Nuevo día, nueva caceria, nuevos pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hackea el planeta!"
|
||||
msgstr "¡Hackea el planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "IA lista."
|
||||
@@ -36,51 +37,51 @@ msgstr "La red neuronal está lista."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Oye, el canal {channel} está libre! Tú AP lo agradecerá."
|
||||
msgstr "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Estoy aburrido ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos por un paseo!"
|
||||
msgstr "¡Vamos por un paseo!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Este es el mejor día de mi vida!"
|
||||
msgstr "¡Este es el mejor día de mi vida!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Día de mierda :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Estoy extremadamente aburrido ..."
|
||||
msgstr "Estoy muy aburrido ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Estoy muy triste ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Estoy triste."
|
||||
msgstr "Estoy triste"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Estoy viviendo la vida!"
|
||||
msgstr "¡Estoy viviendo la vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Pwneo, por lo tanto, existo"
|
||||
msgstr "Pwneo, luego existo."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Cuantas redes!!!"
|
||||
msgstr "¡¡¡Cuántas redes!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Me estoy divirtiendo mucho!"
|
||||
msgstr "¡Me estoy divirtiendo mucho!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Mi único crimen es la curiosidad ..."
|
||||
msgstr "Mi crimen es la curiosidad ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Hola {name}! encantado de conocerte."
|
||||
msgstr "¡Hola {name}! Encantado de conocerte. {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "La unidad {name} está cerca!"
|
||||
msgstr "¡La unidad {name} está cerca! {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
@@ -92,14 +93,14 @@ msgstr "{name} se fue ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Uy ... {name} se fue"
|
||||
msgstr "Ups ... {name} se fue."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} perdido!"
|
||||
msgstr "¡{name} perdido!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Perdido!"
|
||||
msgstr "¡Perdido!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Nadie quiere jugar conmigo ..."
|
||||
@@ -108,11 +109,11 @@ msgid "I feel so alone ..."
|
||||
msgstr "Me siento tan solo ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Dónde está todo el mundo?"
|
||||
msgstr "¡¿Dónde está todo el mundo?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Tomándo una siesta por {secs}s ..."
|
||||
msgstr "Descansando durante {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
@@ -133,23 +134,23 @@ msgstr "Esperando {secs}s .."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Mirando al rededor ({secs}s)"
|
||||
msgstr "Mirando alrededor ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Oye {what} seamos amigos!"
|
||||
msgstr "¡Oye {what} seamos amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Asociando a {what}"
|
||||
msgstr "Asociándome a {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Ey {what}!"
|
||||
msgstr "¡Ey {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Acabo de decidir que {mac} no necesita WiFi!"
|
||||
msgstr "¡Acabo de decidir que {mac} no necesita WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
@@ -157,14 +158,14 @@ msgstr "Desautenticando a {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Expulsando y banneando a {mac}!"
|
||||
msgstr "¡Expulsando y baneando a {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Genial, obtuvimos {num} nuevo{plural} handshake{plural}!"
|
||||
msgstr "¡Genial, obtuvimos {num} nuevo{plural} handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, algo salió mal ... Reiniciándo ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, algo salió mal ... Reiniciando ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
@@ -172,27 +173,27 @@ msgstr "Expulsamos {num} estaciones\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Hicimos {num} nuevos amigos\n"
|
||||
msgstr "Hice {num} nuevos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Obtuvimos {num} handshakes\n"
|
||||
msgstr "Consegui {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Conocí 1 igual"
|
||||
msgstr "Conocí 1 colega"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Conocí {num} iguales"
|
||||
msgstr "Conocí {num} colegas"
|
||||
|
||||
#, 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"
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi #pwnlog "
|
||||
"#pwnlife #hacktheplanet #skynet"
|
||||
msgstr ""
|
||||
"He estado pwneando por {duration} y expulsé {deauthed} clientes! También conocí"
|
||||
"{associated} nuevos amigos y me comí {handshakes} handshakes! #pwnagotchi "
|
||||
"¡He estado pwneando por {duration} y expulsé {deauthed} clientes! También "
|
||||
"conocí {associated} nuevos amigos y comí {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
|
Binary file not shown.
@@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# FIRST AUTHOR <7271496+quantumsheep@users.noreply.github.com>, 2019.
|
||||
#
|
||||
#,
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-23 18:37+0200\n"
|
||||
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
|
||||
"PO-Revision-Date: 2019-10-03 10:34+0200\n"
|
||||
"Last-Translator: quantumsheep <7271496+quantumsheep@users.noreply.github."
|
||||
"com>\n"
|
||||
@@ -43,6 +42,13 @@ msgstr "Génération des clés, ne pas éteindre..."
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hey, le canal {channel} est libre! Ton point d'accès va te remercier."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Lecture des logs de la dernière session ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Jusqu'ici, {lines_so_far} lignes lues dans le log ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Je m'ennuie..."
|
||||
|
||||
@@ -64,8 +70,15 @@ msgstr "Je suis très triste..."
|
||||
msgid "I'm sad"
|
||||
msgstr "Je suis triste"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Lache moi..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Je t'en veux !"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Je vis la vie !"
|
||||
msgstr "Je vis la belle vie !"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Je pwn donc je suis."
|
||||
@@ -83,6 +96,14 @@ msgstr "Mon crime, c'est la curiosité..."
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Bonjour {name} ! Ravi de te rencontrer."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Yo {name} ! Quoi de neuf ?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Hey {name} comment vas-tu ?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "L'unité {name} est proche !"
|
||||
@@ -93,7 +114,7 @@ msgstr "Hum... au revoir {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} est part ..."
|
||||
msgstr "{name} est parti ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
@@ -106,6 +127,12 @@ msgstr "{name} raté !"
|
||||
msgid "Missed!"
|
||||
msgstr "Raté !"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Les bons amis sont une bénédiction !"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "J'aime mes amis !"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Personne ne veut jouer avec moi..."
|
||||
|
||||
@@ -117,14 +144,14 @@ msgstr "Où est tout le monde ?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Fais la sieste pendant {secs}s..."
|
||||
msgstr "Je fais la sieste pendant {secs}s..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr ""
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr ""
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Bonne nuit."
|
||||
@@ -134,11 +161,11 @@ msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Attends pendant {secs}s..."
|
||||
msgstr "J'attends pendant {secs}s..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Regarde autour ({secs}s)"
|
||||
msgstr "J'observe ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
@@ -166,13 +193,13 @@ msgstr "Je kick et je bannis {mac} !"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Cool, on a {num} nouveaux handshake{plural} !"
|
||||
msgstr "Cool, on a {num} nouve(l/aux) handshake{plural} !"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Tu as {num} nouveaux message{plural} !"
|
||||
msgstr "Tu as {num} nouveau(x) message{plural} !"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oups, quelque chose s'est mal passé... Redémarrage..."
|
||||
|
||||
#, python-brace-format
|
||||
@@ -181,18 +208,18 @@ msgstr "{num} stations kick\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Fait {num} nouveaux amis\n"
|
||||
msgstr "A fait {num} nouve(l/aux) ami(s)\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Récupéré {num} handshakes\n"
|
||||
msgstr "A {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "1 peer rencontré"
|
||||
msgstr "1 camarade rencontré"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "{num} peers recontrés"
|
||||
msgstr "{num} camarades recontrés"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
|
Binary file not shown.
@@ -164,7 +164,7 @@ msgstr "Chiceáil mé agus cosc mé ar {mac}!"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Go hiontach, fuaireamar {num} handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Hoips...Tháinig ainghléas éigin..."
|
||||
|
||||
#, python-brace-format
|
||||
|
BIN
pwnagotchi/locale/hu/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/hu/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
249
pwnagotchi/locale/hu/LC_MESSAGES/voice.po
Normal file
249
pwnagotchi/locale/hu/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,249 @@
|
||||
# Hungarian translation.
|
||||
# Copyright (C) 2020
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Skeleton022 <skeleton022.pwnagotchi@gmail.com>, 2020.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.4.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-01-07 20:00+0100\n"
|
||||
"PO-Revision-Date: 2020-03-23 0:10+0100\n"
|
||||
"Last-Translator: Skeleton022\n"
|
||||
"Language-Team: Skeleton022\n"
|
||||
"Language: hungarian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hali, Pwnagotchi vagyok! Indítás ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Új nap, új vadászat, új hálózatok!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Törd meg a bolygót!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "MI kész."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "A neurális hálózat készen áll."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Kulcspár generálása, ne kapcsold ki az eszközt ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "A {channel}. számú csatorna üres! Az AP-d meg fogja köszönni."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Az utolsó munkamenet logjainak olvasása ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Unatkozom ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Menjünk sétálni!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Ez a legjobb nap az életemben!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Szar egy nap :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Nagyon unatkozom ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Nagyon szomorú vagyok ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Szomorú vagyok"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Hagyj békén ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Mérges vagyok rád!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Élvezem az életet!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Hackelek, tehát vagyok."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Rengeteg hálózat!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Nagyon jól érzem magam!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Kíváncsiság a bűnöm ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hali {name}! Örülök, hogy találkoztunk."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Hé {name}! Mizu?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Hé {name} hogy vagy?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "A {name} nevű egység a közelben van!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Ömm ... ég veled {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} eltűnt ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Whoops ... {name} eltűnt."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} elhibázva!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Elvesztettem!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "A jó barátok áldás az életben!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Szeretem a barátaimat!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Senki sem akar játszani velem ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Egyedül vagyok ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Hol vagytok?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "{secs} másodpercig szundikálok ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}msp)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Jó éjszakát."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Várok {secs} másodpercig ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Körbenézek {secs} másodpercig"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hey {what} legyünk barátok!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Társítás {what} -hoz/-hez"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Hé {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Úgydöntöttem, hogy {mac}-nek nem kell WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Kirúgom {mac}-et"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "{mac} kirúgva és kitiltva!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Király, kaptunk {num} új üzenetet!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "{count} új üzeneted van!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, valami rosszul sikerült ... Újraindítás ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Kirúgva {num} állomás\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "{num} új barátot\ntaláltam\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "{num} kézfogást szereztem\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "1 Társsal találkoztam"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Találkoztam {num} társsal"
|
||||
|
||||
#, 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 ""
|
||||
"Már {duration} ideje dolgozom, kirúgtam {deauthed} klienst! Találkoztam még"
|
||||
"{associated} új baráttal és elfogtam {handshakes} kézfogást! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "óra"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "perc"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "másodperc"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "óra"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "perc"
|
||||
|
||||
msgid "second"
|
||||
msgstr "másodperc"
|
||||
|
Binary file not shown.
@@ -156,7 +156,7 @@ msgstr "Sto prendendo a calci {mac}!"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Bene, abbiamo {num} handshake{plural} in più!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, qualcosa è andato storto ... Riavvio ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
Binary file not shown.
@@ -3,12 +3,11 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR 24534649+wytshadow@users.noreply.github.com, 2019.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-16 15:05+0200\n"
|
||||
"POT-Creation-Date: 2020-01-25 21:57+0900\n"
|
||||
"PO-Revision-Date: 2019-10-16 15:05+0200\n"
|
||||
"Last-Translator: wytshadow <24534649+wytshadow@users.noreply.github.com>\n"
|
||||
"Language-Team: pwnagotchi <24534649+wytshadow@users.noreply.github.com>\n"
|
||||
@@ -21,170 +20,207 @@ msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "すやすや〜"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "こんにちは、ポウナゴッチです!始めている。。。"
|
||||
msgstr "僕、 ポーナゴッチです!"
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr ""
|
||||
msgstr "ポーンしようよ。"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "ハックザプラネット!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "人工知能の準備ができました。"
|
||||
msgstr "AIの準備ができました。"
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "ニューラルネットワークの準備ができました。"
|
||||
msgstr "ニューラルネットワークの\n準備ができました。"
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "鍵生成をしてます。\n電源を落とさないでね。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "ねえ、チャンネル{channel}は無料です! キミのAPは感謝を言います。"
|
||||
msgstr "チャンネル\n {channel} \nはfreeだよ。ありがとうね。"
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "session log を読んでます。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "{lines_so_far} 行目長いよぉ。"
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "退屈です。。。"
|
||||
msgstr "退屈だぁ。。。"
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "散歩に行きましょう!"
|
||||
msgstr "散歩に行こうよ!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "今日は私の人生で最高の日です!"
|
||||
msgstr "人生最高の日だよ!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr ""
|
||||
msgstr "がっかりな日だよ。orz"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "とても退屈です。"
|
||||
msgstr "退屈だね。"
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "とても悲しいです。。。"
|
||||
msgstr "あ~悲しいよぉ。"
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "悲しいです。"
|
||||
msgstr "悲しいね。"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "ひとりぼっちだよ。"
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "怒っちゃうよ。"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "人生を生きている!"
|
||||
msgstr "わくわくするね。"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr ""
|
||||
msgstr "ポーンしてこそのオレ。"
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "たくさんネットワークがある!!!"
|
||||
msgstr "たくさん\nWiFiが飛んでるよ!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "とても楽しんでいます!"
|
||||
msgstr "楽しいよぉ!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr ""
|
||||
msgstr "APに興味津々..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "こんにちは{name}!初めまして。{name}"
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "こんにちは{name}!\n初めまして。{name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr ""
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "ねぇねぇ、\n{name} どうしたの?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "{name} こんにちは"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "{name} が近くにいるよ。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "ええと。。。さようなら{name}"
|
||||
msgstr "じゃあね、さようなら {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name}がなくなった。。。"
|
||||
msgstr "{name}\nがいなくなったよ。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "おっと。。。{name}がなくなった。"
|
||||
msgstr "あらら、\n{name}\nがいなくなったね。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name}逃した!"
|
||||
msgstr "{name} が逃げた!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "逃した!"
|
||||
msgstr "残念、逃した!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "良い仲間にめぐりあえたよ。"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "友達は大好きだよ。"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "誰も僕と一緒にプレーしたくない。。。"
|
||||
msgstr "誰も僕と一緒に\nあそんでくれない。"
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "僕は孤独を感じる。。。"
|
||||
msgstr "ひとりぼっちだよ。"
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "みんなどこ?!"
|
||||
msgstr "みんなどこにいるの?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "{secs}寝ている。"
|
||||
msgstr "{secs}秒 寝ます。"
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "すや〜"
|
||||
msgstr "ぐぅ〜"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "すやすや〜 ({secs})"
|
||||
msgstr "すやすや〜 ({secs}秒)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "お休みなさい。"
|
||||
msgstr "おやすみなさい。"
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "す〜"
|
||||
msgstr "ぐぅ~"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "{secs}を待っている。。。"
|
||||
msgstr "{secs}秒 待ちです。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "{secs}を探している。"
|
||||
msgstr "{secs}秒 探してます。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "ちょっと{what}友だちになりましょう!"
|
||||
msgstr "ねぇねぇ\n{what} \n友だちになろうよ。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr ""
|
||||
msgstr "{what} \nとつながるかな?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "よー{what}!"
|
||||
msgstr "ねぇねぇ\n{what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr ""
|
||||
msgstr "{mac}\nはWiFiじゃないのね。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr ""
|
||||
msgstr "{mac}\nの認証取得中..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr ""
|
||||
msgstr "{mac}\nに拒否られた。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "よし、{num}新しいハンドシェイクがある!"
|
||||
msgstr "おぉ、\n{num}回\nハンドシェイクがあったよ!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "おぉ、\n{count}個メッセージがあるよ!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "おっと!何かが間違っていた。。。リブートしている。。。"
|
||||
msgstr "何か間違った。\nリブートしている。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr ""
|
||||
msgstr "{num}回拒否された。\n"
|
||||
|
||||
msgid "Made >999 new friends\n"
|
||||
msgstr "1000人以上友達ができた。\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "{num}人の新しい友達を作りました\n"
|
||||
msgstr "{num}人友達ができた。\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "{num}ハンドシェイクがある。\n"
|
||||
msgstr "{num}回ハンドシェイクした。\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "1人の仲間を会いました。"
|
||||
msgstr "1人 仲間に会いました。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "{num}人の仲間を会いました。"
|
||||
msgstr "{num}人 仲間に会いました。"
|
||||
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
@@ -192,6 +228,9 @@ msgid ""
|
||||
"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
msgstr ""
|
||||
"{duration}中{deauthed}のAPに拒否されたけど、{associated}回チャンスがあって"
|
||||
"{handshakes}回ハンドシェイクがあったよ。。 #pwnagotchi #pwnlog #pwnlife "
|
||||
"#hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "時間"
|
||||
@@ -203,7 +242,7 @@ msgid "seconds"
|
||||
msgstr "秒"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "時間"
|
||||
msgstr "時"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "分"
|
||||
|
Binary file not shown.
@@ -158,7 +158,7 @@ msgstr "Кикбан {mac}!"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Кул, фативме {num} нови ракувања!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Упс, нешто не еко што треба ... Рестартирам ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
Binary file not shown.
@@ -20,7 +20,7 @@ msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hoi, Ik ben Pwnagotchi! Opstarten ..."
|
||||
msgstr "Hoi, Ik ben Pwnagotchi! Aan het opstarten ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Nieuwe dag, nieuwe jacht, nieuwe pwns!"
|
||||
@@ -32,7 +32,7 @@ msgid "AI ready."
|
||||
msgstr "AI is klaar."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Neuronen netwerkis klaar voor gebruik."
|
||||
msgstr "Neuronen netwerk is klaar."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
@@ -42,37 +42,37 @@ msgid "I'm bored ..."
|
||||
msgstr "Ik verveel me ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Laten we een rondje lopen!"
|
||||
msgstr "Laten we gaan wandelen!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Dit is de beste dag van mijn leven!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Ruk dag :/"
|
||||
msgstr "Wat een rotdag :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Ik verveel me kapot ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Ik ben ergverdrietig ..."
|
||||
msgstr "Ik ben erg verdrietig ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Ik ben verdrietig"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Beter kan het levenniet worden!"
|
||||
msgstr "Beter kan het leven niet worden!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Ik pwn daarom besta ik."
|
||||
msgstr "Ik pwn daarom ben ik er."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Zo veel netwerken!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Dit is zo leuk!"
|
||||
msgstr "Ik heb zoveel plezier!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Mijn enige misdrijf is mijn nieuwsgierigheid ..."
|
||||
msgstr "Mijn misdrijf is mijn nieuwsgierigheid ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
@@ -88,11 +88,11 @@ msgstr "Uhm ...tot ziens {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} is weg"
|
||||
msgstr "{name} is weg ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Whoopsie ...{name} is weg"
|
||||
msgstr "Whoopsie ...{name} is weg."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
@@ -102,10 +102,10 @@ msgid "Missed!"
|
||||
msgstr "Gemist!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Niemand wil metmij spelen ..."
|
||||
msgstr "Niemand wil met mij spelen ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Zo alleen ..."
|
||||
msgstr "Ik voel me zo alleen ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Waar is iedereen?!"
|
||||
@@ -119,11 +119,11 @@ msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr ""
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Even {secs}s wachten ..."
|
||||
msgstr "Ik wacht voor {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
@@ -131,7 +131,7 @@ msgstr "Rond kijken ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hey {what}, laten we vriendenworden!"
|
||||
msgstr "Hey {what}, laten we vrienden worden!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
@@ -139,26 +139,26 @@ msgstr "Verbinden met {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr ""
|
||||
msgstr "Yo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Ik vind dat {mac} genoeg WiFiheeft gehad!"
|
||||
msgstr "Ik besloot dat {mac} geen WiFi meer nodig heeft!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "De-autoriseren {mac}"
|
||||
msgstr "Deauthenticatie van {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Ik ga {mac} even kicken!"
|
||||
msgstr "Kickbanning {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Gaaf, we hebben {num} nieuwe handshake{plural}!"
|
||||
msgstr "Cool, we hebben {num} nieuwe handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, iets ging fout ...Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, er ging iets fout ...Rebooting ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
@@ -166,11 +166,11 @@ msgstr "{num} stations gekicked\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "{num} nieuwe vrienden\n"
|
||||
msgstr "{num} nieuwe vrienden gemaakt.\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "{num} nieuwe handshakes\n"
|
||||
msgstr "Ik heb {num} nieuwe handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "1 peer ontmoet"
|
||||
@@ -190,19 +190,19 @@ msgstr ""
|
||||
"gegeten! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr ""
|
||||
msgstr "uren"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
msgstr "minuten"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr ""
|
||||
msgstr "seconden"
|
||||
|
||||
msgid "hour"
|
||||
msgstr ""
|
||||
msgstr "uur"
|
||||
|
||||
msgid "minute"
|
||||
msgstr ""
|
||||
msgstr "minuut"
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
msgstr "seconde"
|
||||
|
BIN
pwnagotchi/locale/no/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/no/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
248
pwnagotchi/locale/no/LC_MESSAGES/voice.po
Normal file
248
pwnagotchi/locale/no/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,248 @@
|
||||
# pwnagotchi norwegian voice data
|
||||
# Copyright (C) 2019
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR untech <edvbot@gmail.com>, 2019.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-04 12:57+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Edvard Botten <edvbot@gmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: norwegian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hei, jeg er Pwnagotchi! Starter ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "En ny dag, ny jakt, og nye pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack planeten!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI klart."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Det nevrale nettverket er klart."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generer nøkkler, ikke skru meg av ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hei, kanalen {channel} er åpen! AP-en din takker."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Leser forrige sesjonen's logs ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Har lest {lines_so_far} linjer hittil ..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Kjeder meg ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "La oss stikke på tur!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Dette er den beste dagen i mitt liv!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Jævlig dag :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Kjeder livet av meg ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Jeg er veldig trist ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Jeg er trist ..."
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "La meg være alene ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Jeg er sint på deg!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Lever livet, lett!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Jeg pwner derfor er jeg."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Så mange nettverk!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Jeg har det så gøy!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Nysgjerrighet er min eneste forbrytelse ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hallo {name}! Hyggelig å treffe deg!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Yo {name}! Skjer'a?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Heisann {name} driver du med da?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "{name} er i nærheten!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... Ha det {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} er borte ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Oi da ... {name} forsvant."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} bommet!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Bommet!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Gode venner er livet verdt!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Jeg digger vennene mine!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Ingen vil leke med meg ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Jeg er så ensom ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Hvor er alle sammen?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Sover i {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "God natt."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Venter i {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Ser meg rundt ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hei {what} la oss være venner!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Tilkobler til {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Bestemte meg att {mac} ikke lenger trenger WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Kobler av {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Kickbanner {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Fett, vi fikk {num} nye håndtrykk!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Du har {count} melding{plural}!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oi, noe gikk helt skakk ... Rebooter ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Kicket {num} stasjoner\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Møtte {num} nye venner\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Skaffet {num} håndtrykk\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Møtte 1 annen"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Møtte {num} andre"
|
||||
|
||||
#, 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 "Jeg har pwnet for {duration} og kicket {dauthed} klienter! Jeg har også "
|
||||
"møtt {associated} nye venner og spiste {handshakes} håndtrykk! #pwnagotchi "
|
||||
"#pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "timer"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutter"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "sekunder"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "time"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minutt"
|
||||
|
||||
msgid "second"
|
||||
msgstr "sekund"
|
Binary file not shown.
@@ -197,7 +197,7 @@ msgstr "Super, zdobyliśmy {num} nowych handshake'ów!"
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Masz {count} nowych wiadomości!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ups, coś poszło nie tak ... Restaruję ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
Binary file not shown.
@@ -158,7 +158,7 @@ msgstr "Kickbanning {mac}"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Legal, nos capturamos {num} handshake{plural} novo{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, algo falhou ... Reiniciando ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
Binary file not shown.
@@ -164,7 +164,7 @@ msgstr "A chutar {mac}!"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Porreiro, temos {num} novo handshake{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ups, algo correu mal ... A reiniciar ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
BIN
pwnagotchi/locale/ro/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/ro/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
249
pwnagotchi/locale/ro/LC_MESSAGES/voice.po
Normal file
249
pwnagotchi/locale/ro/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,249 @@
|
||||
# Pwnagotchi translation.
|
||||
# Copyright (C) 2019
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# FIRST AUTHOR <radu.ungureanu@techie.com>, 2019.
|
||||
#
|
||||
#,
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-04 12:57+0100\n"
|
||||
"PO-Revision-Date: 2019-11-20 00:18+594\n"
|
||||
"Last-Translator: Ungureanu Radu-Andrei <radu.ungureanu@techie.com>\n"
|
||||
"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github."
|
||||
"com>\n"
|
||||
"Language: ro\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 "Buna, sunt Pwnagotchi! Pornesc..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "O noua zi, o noua vanatoare, noi pwn-uri!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Pirateaza planeta!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI-ul e gata."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Rețeaua neuronală este gata."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Se generează chei, nu închide..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hey, canalul {channel} este liber! AP-ul tău îti va mulțumi."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Se citesc log-urile din sesiunea anterioara..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Am citit {lines_so_far} linii din log pana acum..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Sunt plictisit..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Hai să ne plimbăm!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Asta este cea mai buna zi din viața mea!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "O zi proasta :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Sunt extrem de plictisit..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Sunt foarte trist..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Sunt trist"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Lasă-mă in pace..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Sunt supărat pe tine!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Trăiesc viața!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Eu pwn-ez, deci aici sunt."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Atât de multe rețele!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Mă distrez așa de mult!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Crima mea este una de curiozitate..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Bună {name}! Mă bucur să te cunosc."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Yo {name}! Cmf?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Hey {nume} ce mai faci?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Unitatea {name} este aproape!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm... Pa {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} a dispărut."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Oops... {name} a dispărut."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} ratat!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Ratat!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Prietenii buni sunt o binecuvântare!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Îmi iubesc prietenii!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Nimeni nu vrea sa se joace cu mine..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Mă simt așa de singuratic..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Unde-i toată lumea?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Dorm pentru {secs}s..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Noapte bună."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Aștept pentru {secs}s..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Mă uit împrejur ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hey {what} hai să fim prieteni!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Mă asociez cu {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Yo {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Am decis că lui {mac} nu-i trebuie WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Îl deautentific pe {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Îi dau kickban lui {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Șmecher, avem {num} de handshake-uri noi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Ai {count} mesaj(e) nou/noi!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "OOps, ceva s-a întamplat... Îmi dau reboot...+"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Am dat afară {num} de stații\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Am făcut {num} prieteni noi \n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Am primit {num} de handshake-uri\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Am întalnit un peer"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Am întalnit {num} de peer-uri"
|
||||
|
||||
#, 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 "Eu am făcut pwning pentru {duration} și am dat afara {deauthed} clienți! "
|
||||
"De asemenea, am întalnit {associated} prieteni noi și am mancat {handshakes} de "
|
||||
"handshake-uri! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "ore"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minute"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "secunde"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "oră"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minut"
|
||||
|
||||
msgid "second"
|
||||
msgstr "secundă"
|
Binary file not shown.
@@ -1,46 +1,58 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Pwnagotchi Russian translation.
|
||||
# Copyright (C) 2019
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# FIRST AUTHOR <25989971+adolfaka@users.noreply.github.com>, 2019.
|
||||
#
|
||||
# Second author <https://github.com/mbgroot>, 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"
|
||||
"Project-Id-Version: Pwnagotchi Russian translation v 0.0.2\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"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Language: ru\n"
|
||||
"X-Generator: Poedit 2.2.4\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
"X-Poedit-Basepath: .\n"
|
||||
"X-Poedit-SearchPath-0: voice.po\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
msgstr "Хрррр..."
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Привет, я Pwnagotchi! Поехали …"
|
||||
msgstr "Привет, я Pwnagotchi! Стартуем!"
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Новый день, новая охота, новые взломы!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Хак зе планет!"
|
||||
msgstr "Взломай эту Планету!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI готов."
|
||||
msgstr "A.I. готов."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Нейронная сеть готова."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Генерация ключей, не выключайте..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Эй, канал {channel} свободен! Ваша точка доступа скажет спасибо."
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Чтение логов последнего сеанса..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "Чтение {lines_so_far} строк журнала..."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Мне скучно …"
|
||||
|
||||
@@ -62,8 +74,14 @@ msgstr "Мне очень грустно …"
|
||||
msgid "I'm sad"
|
||||
msgstr "Мне грустно"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Оставь меня в покое..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Я зол на тебя!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Угараю по полной!"
|
||||
msgstr "Живу полной жизнью!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Я взламываю, поэтому я существую."
|
||||
@@ -75,15 +93,23 @@ msgid "I'm having so much fun!"
|
||||
msgstr "Мне так весело!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Моe преступление - это любопытство …"
|
||||
msgstr "Моё преступление - это любопытство…"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you. {name}"
|
||||
msgstr "Привет, {name}! Приятно познакомиться. {name}"
|
||||
msgstr "Привет, {name}! Рад встрече с тобой!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby! {name}"
|
||||
msgstr "Цель {name} близко! {name}"
|
||||
msgstr "Цель {name} близко!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Хэй {name}! Как дела?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Цель {name} рядом!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
@@ -91,11 +117,11 @@ msgstr "Хм … до свидания {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} исчезла …"
|
||||
msgstr "{name} ушла…"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Упс … {name} исчезла."
|
||||
msgstr "Упс… {name} исчезла."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
@@ -104,11 +130,17 @@ msgstr "{name} упустил!"
|
||||
msgid "Missed!"
|
||||
msgstr "Промахнулся!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Хорошие друзья - это благословение!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Я люблю своих друзей!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Никто не хочет со мной играть ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Мне так одиноко …"
|
||||
msgstr "Я так одинок…"
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Где все?!"
|
||||
@@ -118,11 +150,17 @@ msgid "Napping for {secs}s ..."
|
||||
msgstr "Дремлет {secs}с …"
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
msgstr "Хррр..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}c)"
|
||||
msgstr "Хррррр.. ({secs}c)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Доброй ночи."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Хрррр"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
@@ -130,7 +168,7 @@ msgstr "Ждем {secs}c …"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Оглядываюсь вокруг ({secs}с)"
|
||||
msgstr "Осматриваюсь вокруг ({secs}с)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
@@ -160,7 +198,7 @@ msgstr "Кикаю {mac}!"
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Круто, мы получили {num} новое рукопожатие!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ой, что-то пошло не так … Перезагружаюсь …"
|
||||
|
||||
#, python-brace-format
|
||||
@@ -173,7 +211,7 @@ msgstr "Заимел {num} новых друзей\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Получил {num} рукопожатие\n"
|
||||
msgstr "Получил {num} рукопожатий\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Встретился один знакомый"
|
||||
|
Binary file not shown.
@@ -158,7 +158,7 @@ msgstr ""
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Lysande, vi har {num} ny handskakningar{plural}!"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Hoppsan, någpt gick fel ... Startar om ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
BIN
pwnagotchi/locale/sk/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/sk/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
227
pwnagotchi/locale/sk/LC_MESSAGES/voice.po
Normal file
227
pwnagotchi/locale/sk/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,227 @@
|
||||
# Slovak language
|
||||
# Copyright (C) 2019
|
||||
# This file is distributed under the same license as the pwnagotchi package.
|
||||
# mil1200 <mil.kyselica@gmail.com>, 2019.
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-8 17:55+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Milan Kyselica <mil.kyselica@gmail.com>\n"
|
||||
"Language-Team: SK\n"
|
||||
"Language: sk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Ahoj, ja som Pwnagotchi! Začíname ..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Nový deň, nový lov, nové pwns!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hacknime Planétu!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "AI pripravené."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "Neurónová sieť je pripravená."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generujú sa kľúče, nevypínaj ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hej, kanál {channel} je voľný! Váš AP vám poďakuje."
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Nudím sa ..."
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Poďme na prechádzku!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Toto je najlepší deň môjho života!"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Na hovno deň :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Veľmi sa nudím ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Som veľmi smutný ..."
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Som smutný"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Žijem život!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "I pwn therefore I am."
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Toľko sietí !!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Zabávam sa!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Môj zločin je zvedavosť ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Dobrý deň, {name}! Rád som ťa spoznal."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Jednotka {name} je blízko!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... zbohom {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} je preč ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Hups ... {name} je preč."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} nechytené!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Vedľa!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Dobrí priatelia sú požehnaním!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Milujem svojich priateľov!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Nikto sa so mnou nechce hrať ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Cítim sa tak sám ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Kde sú všetci ?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Zdriemnem si na {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Dobrú noc."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Čaká sa {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Rozhliadam sa okolo ({secs} s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Ahoj {what} buďme priatelia!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Spájam sa s {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Yo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Rozhodol som sa že {mac} nepotrebuje Wi-Fi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "Deautentifikujem {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Kickujem {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Super, máme {num} nový handshake{plural}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Máte {count} novú správu{plural}!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ops, niečo sa pokazilo ... Reštartujem sa ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Kicknutá/ých {num} stanica/íc\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Získaní {num} noví kamaráti\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Získali sme {num} handshake/-y/ov rúk\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Sretli sme 1 rovesníka"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "Stretli sme {num} rovesníkov"
|
||||
|
||||
#, 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 "Pwnoval som {duration} a kickol som {deauthed} klienta/ov! Tiež som"
|
||||
"stretol {associated} nového/ých kamaráta/ov a zjedol {handshakes} handshake/y!"
|
||||
" #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "hodiny"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minúty"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "sekundy"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hodina"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minúta"
|
||||
|
||||
msgid "second"
|
||||
msgstr "sekunda"
|
BIN
pwnagotchi/locale/spa/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/spa/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
248
pwnagotchi/locale/spa/LC_MESSAGES/voice.po
Normal file
248
pwnagotchi/locale/spa/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,248 @@
|
||||
# Interfaz en español para pwnagotchi
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Angel Hernandez Segura, 2019.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-04 12:57+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Angel Hernandez Segura <ahsec.7@gmail.com>\n"
|
||||
"Language-Team: Español <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "Hola, Soy Pwnagotchi! Iniciando..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "Un nuevo dia, nuevos objetivos, nuevos pwns"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "Hack the Planet!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "IA lista"
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "La red neuronal esta lista"
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "Generando llaves, no apagar"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "Hey, canal {channel} esta libre! Tu AP te lo agredecera"
|
||||
|
||||
msgid "Reading last session logs ..."
|
||||
msgstr "Leyendo logs de la ultima sesion"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Read {lines_so_far} log lines so far ..."
|
||||
msgstr "He leido {lines_so_far} lineas de los logs hasta ahora "
|
||||
|
||||
msgid "I'm bored ..."
|
||||
msgstr "Estoy aburrido"
|
||||
|
||||
msgid "Let's go for a walk!"
|
||||
msgstr "Vamos a caminar!"
|
||||
|
||||
msgid "This is the best day of my life!"
|
||||
msgstr "Este es el mejor dia de mi vida"
|
||||
|
||||
msgid "Shitty day :/"
|
||||
msgstr "Dia de mierda :/"
|
||||
|
||||
msgid "I'm extremely bored ..."
|
||||
msgstr "Estoy extremadamente aburrido ..."
|
||||
|
||||
msgid "I'm very sad ..."
|
||||
msgstr "Estoy mut triste"
|
||||
|
||||
msgid "I'm sad"
|
||||
msgstr "Estoy triste"
|
||||
|
||||
msgid "Leave me alone ..."
|
||||
msgstr "Dejame solo ..."
|
||||
|
||||
msgid "I'm mad at you!"
|
||||
msgstr "Estoy enojado contigo!"
|
||||
|
||||
msgid "I'm living the life!"
|
||||
msgstr "Estoy disfrutando la vida!"
|
||||
|
||||
msgid "I pwn therefore I am."
|
||||
msgstr "Yo pwn, por lo tanto existo"
|
||||
|
||||
msgid "So many networks!!!"
|
||||
msgstr "Tantas redes!!!"
|
||||
|
||||
msgid "I'm having so much fun!"
|
||||
msgstr "Me estoy divirtiendo mucho!"
|
||||
|
||||
msgid "My crime is that of curiosity ..."
|
||||
msgstr "Mi crimen es la curiosidad ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hola {name}! Mucho gusto."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {name}! Sup?"
|
||||
msgstr "Yo {name}! Que hay?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {name} how are you doing?"
|
||||
msgstr "Hey {name} como te va?"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "Unit {name} esta cerca!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "Uhm ... adios {name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name} se fue ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "Whoops ... {name} se fue"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "{name} se ha perdido!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "Perdido!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "Los buenos amigos son una bendicion"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "Amo a mis amigos!"
|
||||
|
||||
msgid "Nobody wants to play with me ..."
|
||||
msgstr "Nadie quiere jugar conmigo ..."
|
||||
|
||||
msgid "I feel so alone ..."
|
||||
msgstr "Me siento muy solo ..."
|
||||
|
||||
msgid "Where's everybody?!"
|
||||
msgstr "Donde estan todos?!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Napping for {secs}s ..."
|
||||
msgstr "Tomando una siesta por {secs}s ..."
|
||||
|
||||
msgid "Zzzzz"
|
||||
msgstr "Zzzzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "ZzzZzzz ({secs}s)"
|
||||
msgstr "ZzzZzzz ({secs}s) "
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "Buenas noches."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "Esperando por {secs}s ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "Mirando alrededor ({secs}s)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "Hey {what} vamos a ser amigos!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "Asociandose a {what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "Yo {what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "Acabo de decidir que {mac} no necesita WiFi!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "De-autenticando {mac}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "Vetando {mac}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "Bien, obtuvimos {num} nuevos handshake{plural}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Tienes {count} nuevos mensajes{plural}!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Oops, algo salio mal ... Reiniciando ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kicked {num} stations\n"
|
||||
msgstr "Bloquee {num} staciones\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Made {num} new friends\n"
|
||||
msgstr "Hice {num} nuevos amigos\n"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Got {num} handshakes\n"
|
||||
msgstr "Obtuve {num} handshakes\n"
|
||||
|
||||
msgid "Met 1 peer"
|
||||
msgstr "Conoci a 1 unidad"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Met {num} peers"
|
||||
msgstr "conoci {num} unidades"
|
||||
|
||||
#, 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 "He estado hackeando por {duration} y de-autenticando {deauthed} "
|
||||
"clientes! Tambien conoci {associated} nuevos amigos y comi {handshakes} "
|
||||
"handshakes! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "horas"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "minutos"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "segundos"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "hora"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "minuto"
|
||||
|
||||
msgid "second"
|
||||
msgstr "segundo"
|
BIN
pwnagotchi/locale/tw/LC_MESSAGES/voice.mo
Normal file
BIN
pwnagotchi/locale/tw/LC_MESSAGES/voice.mo
Normal file
Binary file not shown.
227
pwnagotchi/locale/tw/LC_MESSAGES/voice.po
Normal file
227
pwnagotchi/locale/tw/LC_MESSAGES/voice.po
Normal file
@@ -0,0 +1,227 @@
|
||||
# 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 <shark_shaking@hotmail.com>, 2020.
|
||||
# 有很多翻譯不到味,如果有繁體愛好者,歡迎之後大家一起翻譯!
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.0.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-10-21 15:56+0200\n"
|
||||
"PO-Revision-Date: 2020-10-22 10:00+0008\n"
|
||||
"Last-Translator: ShaqKSmith <shark_shaking@hotmail.com>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: traditional chinese\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "ZzzzZZzzzzZzzz"
|
||||
msgstr "ZzzzZZzzzzZzzz"
|
||||
|
||||
msgid "Hi, I'm Pwnagotchi! Starting ..."
|
||||
msgstr "HI!我是Pwnagotchi!\n程式啟動..."
|
||||
|
||||
msgid "New day, new hunt, new pwns!"
|
||||
msgstr "新的一天!\n新的狩獵!新的入侵!"
|
||||
|
||||
msgid "Hack the Planet!"
|
||||
msgstr "我要駭入\n地球的所有人!"
|
||||
|
||||
msgid "AI ready."
|
||||
msgstr "人工智慧已啟動."
|
||||
|
||||
msgid "The neural network is ready."
|
||||
msgstr "神經網路已啟動."
|
||||
|
||||
msgid "Generating keys, do not turn off ..."
|
||||
msgstr "產生金鑰中,\n請勿關閉..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey, channel {channel} is free! Your AP will say thanks."
|
||||
msgstr "嘿,{channel}很順暢!\n你的WIFI會感謝你的."
|
||||
|
||||
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 "我的缺點就是\n太好奇了..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hello {name}! Nice to meet you."
|
||||
msgstr "Hello{name}!\n很高興認識你."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Unit {name} is nearby!"
|
||||
msgstr "{name}\n就在附近!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uhm ... goodbye {name}"
|
||||
msgstr "啊 ... \n拜拜{name}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} is gone ..."
|
||||
msgstr "{name}\n不見了 ..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Whoops ... {name} is gone."
|
||||
msgstr "歐哦...\n{name}\n不見了."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "{name} missed!"
|
||||
msgstr "我剛剛錯過了{name}!"
|
||||
|
||||
msgid "Missed!"
|
||||
msgstr "又錯過了!"
|
||||
|
||||
msgid "Good friends are a blessing!"
|
||||
msgstr "有個好朋友\n真幸福!"
|
||||
|
||||
msgid "I love my friends!"
|
||||
msgstr "我喜歡\n我的朋友!"
|
||||
|
||||
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}秒)"
|
||||
|
||||
msgid "Good night."
|
||||
msgstr "晚安."
|
||||
|
||||
msgid "Zzz"
|
||||
msgstr "Zzz"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Waiting for {secs}s ..."
|
||||
msgstr "等我{secs}秒..."
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Looking around ({secs}s)"
|
||||
msgstr "環顧四周({secs}秒)"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Hey {what} let's be friends!"
|
||||
msgstr "嗨\n{what}\n讓我我們來當朋友吧!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Associating to {what}"
|
||||
msgstr "正在連接\n{what}"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Yo {what}!"
|
||||
msgstr "喲,\n{what}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Just decided that {mac} needs no WiFi!"
|
||||
msgstr "我要讓\n{mac}\n斷線!\n他不需要上網!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Deauthenticating {mac}"
|
||||
msgstr "解除\n{mac}\n的授權中"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Kickbanning {mac}!"
|
||||
msgstr "把\n{mac}\n踢出中!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Cool, we got {num} new handshake{plural}!"
|
||||
msgstr "酷哦,我們抓到{num}個\n新的握手包{plural}!"
|
||||
|
||||
#, python-brace-format
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "你有{count}個新訊息{plural}!"
|
||||
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "喔歐,有些地方出錯了...\n重新啟動中..."
|
||||
|
||||
#, 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 "遇到了 1 個"
|
||||
|
||||
#, 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}的時間\n駭入和踢了{deauthed}好多設備.\n"
|
||||
"我還交了好多{associated}新朋友,\n而且抓到了{handshakes}握手包!"
|
||||
"#pwnagotchi#入侵日志 #駭客人生 #入侵整個星球 #天網"
|
||||
|
||||
msgid "hours"
|
||||
msgstr "時"
|
||||
|
||||
msgid "minutes"
|
||||
msgstr "分"
|
||||
|
||||
msgid "seconds"
|
||||
msgstr "秒"
|
||||
|
||||
msgid "hour"
|
||||
msgstr "時"
|
||||
|
||||
msgid "minute"
|
||||
msgstr "分"
|
||||
|
||||
msgid "second"
|
||||
msgstr "秒"
|
Binary file not shown.
@@ -177,7 +177,7 @@ msgstr "Отакої, у нас є {num} нових рукостискань!"
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr "Нових повідомлень: {count}"
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr "Ой, щось пішло не так ... Перезавантажуюсь ..."
|
||||
|
||||
#, python-brace-format
|
||||
|
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-11-04 12:57+0100\n"
|
||||
"POT-Creation-Date: 2019-11-29 21:50+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -198,7 +198,7 @@ msgstr ""
|
||||
msgid "You have {count} new message{plural}!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Ops, something went wrong ... Rebooting ..."
|
||||
msgid "Oops, something went wrong ... Rebooting ..."
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
@@ -244,3 +244,7 @@ msgstr ""
|
||||
|
||||
msgid "second"
|
||||
msgstr ""
|
||||
|
||||
#, python-brace-format
|
||||
msgid "Uploading data to {to} ..."
|
||||
msgstr ""
|
||||
|
@@ -3,6 +3,9 @@ import time
|
||||
import re
|
||||
import os
|
||||
import logging
|
||||
import shutil
|
||||
import gzip
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
|
||||
from pwnagotchi.voice import Voice
|
||||
@@ -26,7 +29,7 @@ class LastSession(object):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.voice = Voice(lang=config['main']['lang'])
|
||||
self.path = config['main']['log']
|
||||
self.path = config['main']['log']['path']
|
||||
self.last_session = []
|
||||
self.last_session_id = ''
|
||||
self.last_saved_session_id = ''
|
||||
@@ -209,3 +212,98 @@ class LastSession(object):
|
||||
|
||||
def is_new(self):
|
||||
return self.last_session_id != self.last_saved_session_id
|
||||
|
||||
|
||||
def setup_logging(args, config):
|
||||
cfg = config['main']['log']
|
||||
filename = cfg['path']
|
||||
|
||||
formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s")
|
||||
root = logging.getLogger()
|
||||
|
||||
root.setLevel(logging.DEBUG if args.debug else logging.INFO)
|
||||
|
||||
if filename:
|
||||
# since python default log rotation might break session data in different files,
|
||||
# we need to do log rotation ourselves
|
||||
log_rotation(filename, cfg)
|
||||
|
||||
file_handler = logging.FileHandler(filename)
|
||||
file_handler.setFormatter(formatter)
|
||||
root.addHandler(file_handler)
|
||||
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
root.addHandler(console_handler)
|
||||
|
||||
if not args.debug:
|
||||
# disable scapy and tensorflow logging
|
||||
logging.getLogger("scapy").disabled = True
|
||||
logging.getLogger('tensorflow').disabled = True
|
||||
# https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning
|
||||
warnings.simplefilter(action='ignore', category=FutureWarning)
|
||||
warnings.simplefilter(action='ignore', category=DeprecationWarning)
|
||||
# https://stackoverflow.com/questions/24344045/how-can-i-completely-remove-any-logging-from-requests-module-in-python?noredirect=1&lq=1
|
||||
logging.getLogger("urllib3").propagate = False
|
||||
requests_log = logging.getLogger("requests")
|
||||
requests_log.addHandler(logging.NullHandler())
|
||||
requests_log.prpagate = False
|
||||
|
||||
|
||||
def log_rotation(filename, cfg):
|
||||
rotation = cfg['rotation']
|
||||
if not rotation['enabled']:
|
||||
return
|
||||
elif not os.path.isfile(filename):
|
||||
return
|
||||
|
||||
stats = os.stat(filename)
|
||||
# specify a maximum size to rotate ( format is 10/10B, 10K, 10M 10G )
|
||||
if rotation['size']:
|
||||
max_size = parse_max_size(rotation['size'])
|
||||
if stats.st_size >= max_size:
|
||||
do_rotate(filename, stats, cfg)
|
||||
else:
|
||||
raise Exception("log rotation is enabled but log.rotation.size was not specified")
|
||||
|
||||
|
||||
def parse_max_size(s):
|
||||
parts = re.findall(r'(^\d+)([bBkKmMgG]?)', s)
|
||||
if len(parts) != 1 or len(parts[0]) != 2:
|
||||
raise Exception("can't parse %s as a max size" % s)
|
||||
|
||||
num, unit = parts[0]
|
||||
num = int(num)
|
||||
unit = unit.lower()
|
||||
|
||||
if unit == 'k':
|
||||
return num * 1024
|
||||
elif unit == 'm':
|
||||
return num * 1024 * 1024
|
||||
elif unit == 'g':
|
||||
return num * 1024 * 1024 * 1024
|
||||
else:
|
||||
return num
|
||||
|
||||
|
||||
def do_rotate(filename, stats, cfg):
|
||||
base_path = os.path.dirname(filename)
|
||||
name = os.path.splitext(os.path.basename(filename))[0]
|
||||
archive_filename = os.path.join(base_path, "%s.gz" % name)
|
||||
counter = 2
|
||||
|
||||
while os.path.exists(archive_filename):
|
||||
archive_filename = os.path.join(base_path, "%s-%d.gz" % (name, counter))
|
||||
counter += 1
|
||||
|
||||
log_filename = archive_filename.replace('gz', 'log')
|
||||
|
||||
print("%s is %d bytes big, rotating to %s ..." % (filename, stats.st_size, log_filename))
|
||||
|
||||
shutil.move(filename, log_filename)
|
||||
|
||||
print("compressing to %s ..." % archive_filename)
|
||||
|
||||
with open(log_filename, 'rb') as src:
|
||||
with gzip.open(archive_filename, 'wb') as dst:
|
||||
dst.writelines(src)
|
||||
|
@@ -17,7 +17,7 @@ class AsyncAdvertiser(object):
|
||||
self._keypair = keypair
|
||||
self._advertisement = {
|
||||
'name': pwnagotchi.name(),
|
||||
'version': pwnagotchi.version,
|
||||
'version': pwnagotchi.__version__,
|
||||
'identity': self._keypair.fingerprint,
|
||||
'face': faces.FRIEND,
|
||||
'pwnd_run': 0,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
NumChannels = 140
|
||||
NumChannelsExt = 165 # see https://github.com/evilsocket/pwnagotchi/issues/583
|
||||
|
||||
|
||||
def freq_to_channel(freq):
|
||||
|
@@ -1,37 +1,102 @@
|
||||
import os
|
||||
import glob
|
||||
import _thread
|
||||
import threading
|
||||
import importlib, importlib.util
|
||||
import logging
|
||||
|
||||
|
||||
|
||||
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
||||
loaded = {}
|
||||
database = {}
|
||||
locks = {}
|
||||
|
||||
|
||||
class Plugin:
|
||||
@classmethod
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
global loaded
|
||||
global loaded, locks
|
||||
|
||||
plugin_name = cls.__module__.split('.')[0]
|
||||
plugin_instance = cls()
|
||||
logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance))
|
||||
loaded[plugin_name] = plugin_instance
|
||||
|
||||
for attr_name in plugin_instance.__dir__():
|
||||
if attr_name.startswith('on_'):
|
||||
cb = getattr(plugin_instance, attr_name, None)
|
||||
if cb is not None and callable(cb):
|
||||
locks["%s::%s" % (plugin_name, attr_name)] = threading.Lock()
|
||||
|
||||
|
||||
def toggle_plugin(name, enable=True):
|
||||
"""
|
||||
Load or unload a plugin
|
||||
|
||||
returns True if changed, otherwise False
|
||||
"""
|
||||
import pwnagotchi
|
||||
from pwnagotchi.ui import view
|
||||
from pwnagotchi.utils import save_config
|
||||
|
||||
global loaded, database
|
||||
|
||||
if pwnagotchi.config:
|
||||
if not name in pwnagotchi.config['main']['plugins']:
|
||||
pwnagotchi.config['main']['plugins'][name] = dict()
|
||||
pwnagotchi.config['main']['plugins'][name]['enabled'] = enable
|
||||
save_config(pwnagotchi.config, '/etc/pwnagotchi/config.toml')
|
||||
|
||||
if not enable and name in loaded:
|
||||
if getattr(loaded[name], 'on_unload', None):
|
||||
loaded[name].on_unload(view.ROOT)
|
||||
del loaded[name]
|
||||
|
||||
return True
|
||||
|
||||
if enable and name in database and name not in loaded:
|
||||
load_from_file(database[name])
|
||||
if name in loaded and pwnagotchi.config and name in pwnagotchi.config['main']['plugins']:
|
||||
loaded[name].options = pwnagotchi.config['main']['plugins'][name]
|
||||
one(name, 'loaded')
|
||||
if pwnagotchi.config:
|
||||
one(name, 'config_changed', pwnagotchi.config)
|
||||
one(name, 'ui_setup', view.ROOT)
|
||||
one(name, 'ready', view.ROOT._agent)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def on(event_name, *args, **kwargs):
|
||||
for plugin_name, plugin in loaded.items():
|
||||
for plugin_name in loaded.keys():
|
||||
one(plugin_name, event_name, *args, **kwargs)
|
||||
|
||||
|
||||
def locked_cb(lock_name, cb, *args, **kwargs):
|
||||
global locks
|
||||
|
||||
if lock_name not in locks:
|
||||
locks[lock_name] = threading.Lock()
|
||||
|
||||
with locks[lock_name]:
|
||||
cb(*args, *kwargs)
|
||||
|
||||
|
||||
def one(plugin_name, event_name, *args, **kwargs):
|
||||
global loaded
|
||||
|
||||
if plugin_name in loaded:
|
||||
plugin = loaded[plugin_name]
|
||||
cb_name = 'on_%s' % event_name
|
||||
callback = getattr(plugin, cb_name, None)
|
||||
if callback is not None and callable(callback):
|
||||
try:
|
||||
callback(*args, **kwargs)
|
||||
lock_name = "%s::%s" % (plugin_name, cb_name)
|
||||
locked_cb_args = (lock_name, callback, *args, *kwargs)
|
||||
_thread.start_new_thread(locked_cb, locked_cb_args)
|
||||
except Exception as e:
|
||||
logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e))
|
||||
logging.error(e, exc_info=True)
|
||||
@@ -47,10 +112,11 @@ def load_from_file(filename):
|
||||
|
||||
|
||||
def load_from_path(path, enabled=()):
|
||||
global loaded
|
||||
global loaded, database
|
||||
logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
|
||||
for filename in glob.glob(os.path.join(path, "*.py")):
|
||||
plugin_name = os.path.basename(filename.replace(".py", ""))
|
||||
database[plugin_name] = filename
|
||||
if plugin_name in enabled:
|
||||
try:
|
||||
load_from_file(filename)
|
||||
@@ -78,3 +144,4 @@ def load(config):
|
||||
plugin.options = config['main']['plugins'][name]
|
||||
|
||||
on('loaded')
|
||||
on('config_changed', config)
|
||||
|
394
pwnagotchi/plugins/cmd.py
Normal file
394
pwnagotchi/plugins/cmd.py
Normal file
@@ -0,0 +1,394 @@
|
||||
# Handles the commandline stuff
|
||||
|
||||
import os
|
||||
import logging
|
||||
import glob
|
||||
import re
|
||||
import shutil
|
||||
from fnmatch import fnmatch
|
||||
from pwnagotchi.utils import download_file, unzip, save_config, parse_version, md5
|
||||
from pwnagotchi.plugins import default_path
|
||||
|
||||
|
||||
SAVE_DIR = '/usr/local/share/pwnagotchi/availaible-plugins/'
|
||||
DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/'
|
||||
|
||||
|
||||
def add_parsers(parser):
|
||||
"""
|
||||
Adds the plugins subcommand to a given argparse.ArgumentParser
|
||||
"""
|
||||
subparsers = parser.add_subparsers()
|
||||
## pwnagotchi plugins
|
||||
parser_plugins = subparsers.add_parser('plugins')
|
||||
plugin_subparsers = parser_plugins.add_subparsers(dest='plugincmd')
|
||||
|
||||
## pwnagotchi plugins search
|
||||
parser_plugins_search = plugin_subparsers.add_parser('search', help='Search for pwnagotchi plugins')
|
||||
parser_plugins_search.add_argument('pattern', type=str, help="Search expression (wildcards allowed)")
|
||||
|
||||
## pwnagotchi plugins list
|
||||
parser_plugins_list = plugin_subparsers.add_parser('list', help='List available pwnagotchi plugins')
|
||||
parser_plugins_list.add_argument('-i', '--installed', action='store_true', required=False, help='List also installed plugins')
|
||||
|
||||
## pwnagotchi plugins update
|
||||
parser_plugins_update = plugin_subparsers.add_parser('update', help='Updates the database')
|
||||
|
||||
## pwnagotchi plugins upgrade
|
||||
parser_plugins_upgrade = plugin_subparsers.add_parser('upgrade', help='Upgrades plugins')
|
||||
parser_plugins_upgrade.add_argument('pattern', type=str, nargs='?', default='*', help="Filter expression (wildcards allowed)")
|
||||
|
||||
## pwnagotchi plugins enable
|
||||
parser_plugins_enable = plugin_subparsers.add_parser('enable', help='Enables a plugin')
|
||||
parser_plugins_enable.add_argument('name', type=str, help='Name of the plugin')
|
||||
|
||||
## pwnagotchi plugins disable
|
||||
parser_plugins_disable = plugin_subparsers.add_parser('disable', help='Disables a plugin')
|
||||
parser_plugins_disable.add_argument('name', type=str, help='Name of the plugin')
|
||||
|
||||
## pwnagotchi plugins install
|
||||
parser_plugins_install = plugin_subparsers.add_parser('install', help='Installs a plugin')
|
||||
parser_plugins_install.add_argument('name', type=str, help='Name of the plugin')
|
||||
|
||||
## pwnagotchi plugins uninstall
|
||||
parser_plugins_uninstall = plugin_subparsers.add_parser('uninstall', help='Uninstalls a plugin')
|
||||
parser_plugins_uninstall.add_argument('name', type=str, help='Name of the plugin')
|
||||
|
||||
## pwnagotchi plugins edit
|
||||
parser_plugins_edit = plugin_subparsers.add_parser('edit', help='Edit the options')
|
||||
parser_plugins_edit.add_argument('name', type=str, help='Name of the plugin')
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def used_plugin_cmd(args):
|
||||
"""
|
||||
Checks if the plugins subcommand was used
|
||||
"""
|
||||
return hasattr(args, 'plugincmd')
|
||||
|
||||
|
||||
def handle_cmd(args, config):
|
||||
"""
|
||||
Parses the arguments and does the thing the user wants
|
||||
"""
|
||||
if args.plugincmd == 'update':
|
||||
return update(config)
|
||||
elif args.plugincmd == 'search':
|
||||
args.installed = True # also search in installed plugins
|
||||
return list_plugins(args, config, args.pattern)
|
||||
elif args.plugincmd == 'install':
|
||||
return install(args, config)
|
||||
elif args.plugincmd == 'uninstall':
|
||||
return uninstall(args, config)
|
||||
elif args.plugincmd == 'list':
|
||||
return list_plugins(args, config)
|
||||
elif args.plugincmd == 'enable':
|
||||
return enable(args, config)
|
||||
elif args.plugincmd == 'disable':
|
||||
return disable(args, config)
|
||||
elif args.plugincmd == 'upgrade':
|
||||
return upgrade(args, config, args.pattern)
|
||||
elif args.plugincmd == 'edit':
|
||||
return edit(args, config)
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def edit(args, config):
|
||||
"""
|
||||
Edit the config of the plugin
|
||||
"""
|
||||
plugin = args.name
|
||||
editor = os.environ.get('EDITOR', 'vim') # because vim is the best
|
||||
|
||||
if plugin not in config['main']['plugins']:
|
||||
return 1
|
||||
|
||||
plugin_config = {'main': {'plugins': {plugin: config['main']['plugins'][plugin]}}}
|
||||
|
||||
import toml
|
||||
from subprocess import call
|
||||
from tempfile import NamedTemporaryFile
|
||||
from pwnagotchi.utils import DottedTomlEncoder
|
||||
|
||||
new_plugin_config = None
|
||||
with NamedTemporaryFile(suffix=".tmp", mode='r+t') as tmp:
|
||||
tmp.write(toml.dumps(plugin_config, encoder=DottedTomlEncoder()))
|
||||
tmp.flush()
|
||||
rc = call([editor, tmp.name])
|
||||
if rc != 0:
|
||||
return rc
|
||||
tmp.seek(0)
|
||||
new_plugin_config = toml.load(tmp)
|
||||
|
||||
config['main']['plugins'][plugin] = new_plugin_config['main']['plugins'][plugin]
|
||||
save_config(config, args.user_config)
|
||||
return 0
|
||||
|
||||
|
||||
def enable(args, config):
|
||||
"""
|
||||
Enables the given plugin and saves the config to disk
|
||||
"""
|
||||
if args.name not in config['main']['plugins']:
|
||||
config['main']['plugins'][args.name] = dict()
|
||||
config['main']['plugins'][args.name]['enabled'] = True
|
||||
save_config(config, args.user_config)
|
||||
return 0
|
||||
|
||||
|
||||
def disable(args, config):
|
||||
"""
|
||||
Disables the given plugin and saves the config to disk
|
||||
"""
|
||||
if args.name not in config['main']['plugins']:
|
||||
config['main']['plugins'][args.name] = dict()
|
||||
config['main']['plugins'][args.name]['enabled'] = False
|
||||
save_config(config, args.user_config)
|
||||
return 0
|
||||
|
||||
|
||||
def upgrade(args, config, pattern='*'):
|
||||
"""
|
||||
Upgrades the given plugin
|
||||
"""
|
||||
available = _get_available()
|
||||
installed = _get_installed(config)
|
||||
|
||||
for plugin, filename in installed.items():
|
||||
if not fnmatch(plugin, pattern) or plugin not in available:
|
||||
continue
|
||||
|
||||
available_version = _extract_version(available[plugin])
|
||||
installed_version = _extract_version(filename)
|
||||
|
||||
if installed_version and available_version:
|
||||
if available_version <= installed_version:
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
|
||||
logging.info('Upgrade %s from %s to %s', plugin, '.'.join(installed_version), '.'.join(available_version))
|
||||
shutil.copyfile(available[plugin], installed[plugin])
|
||||
|
||||
# maybe has config
|
||||
for conf in glob.glob(available[plugin].replace('.py', '.y?ml')):
|
||||
dst = os.path.join(os.path.dirname(installed[plugin]), os.path.basename(conf))
|
||||
if os.path.exists(dst) and md5(dst) != md5(conf):
|
||||
# backup
|
||||
logging.info('Backing up config: %s', os.path.basename(conf))
|
||||
shutil.move(dst, dst + '.bak')
|
||||
shutil.copyfile(conf, dst)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def list_plugins(args, config, pattern='*'):
|
||||
"""
|
||||
Lists the available and installed plugins
|
||||
"""
|
||||
found = False
|
||||
|
||||
line = "|{name:^{width}}|{version:^9}|{enabled:^10}|{status:^15}|"
|
||||
|
||||
available = _get_available()
|
||||
installed = _get_installed(config)
|
||||
|
||||
available_and_installed = set(list(available.keys()) + list(installed.keys()))
|
||||
available_not_installed = set(available.keys()) - set(installed.keys())
|
||||
|
||||
max_len_list = available_and_installed if args.installed else available_not_installed
|
||||
max_len = max(map(len, max_len_list))
|
||||
header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status')
|
||||
line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol
|
||||
|
||||
print('-' * line_length)
|
||||
print(header)
|
||||
print('-' * line_length)
|
||||
|
||||
if args.installed:
|
||||
# only installed (maybe update available?)
|
||||
for plugin, filename in sorted(installed.items()):
|
||||
if not fnmatch(plugin, pattern):
|
||||
continue
|
||||
found = True
|
||||
installed_version = _extract_version(filename)
|
||||
available_version = None
|
||||
if plugin in available:
|
||||
available_version = _extract_version(available[plugin])
|
||||
|
||||
status = "installed"
|
||||
if installed_version and available_version:
|
||||
if available_version > installed_version:
|
||||
status = "installed (^)"
|
||||
|
||||
enabled = 'enabled' if plugin in config['main']['plugins'] and \
|
||||
'enabled' in config['main']['plugins'][plugin] and \
|
||||
config['main']['plugins'][plugin]['enabled'] \
|
||||
else 'disabled'
|
||||
|
||||
print(line.format(name=plugin, width=max_len, version='.'.join(installed_version), enabled=enabled, status=status))
|
||||
|
||||
|
||||
for plugin in sorted(available_not_installed):
|
||||
if not fnmatch(plugin, pattern):
|
||||
continue
|
||||
found = True
|
||||
available_version = _extract_version(available[plugin])
|
||||
print(line.format(name=plugin, width=max_len, version='.'.join(available_version), enabled='-', status='available'))
|
||||
|
||||
print('-' * line_length)
|
||||
|
||||
if not found:
|
||||
logging.info('Maybe try: pwnagotchi plugins update')
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _extract_version(filename):
|
||||
"""
|
||||
Extracts the version from a python file
|
||||
"""
|
||||
plugin_content = open(filename, 'rt').read()
|
||||
m = re.search(r'__version__[\t ]*=[\t ]*[\'\"]([^\"\']+)', plugin_content)
|
||||
if m:
|
||||
return parse_version(m.groups()[0])
|
||||
return None
|
||||
|
||||
|
||||
def _get_available():
|
||||
"""
|
||||
Get all availaible plugins
|
||||
"""
|
||||
available = dict()
|
||||
for filename in glob.glob(os.path.join(SAVE_DIR, "*.py")):
|
||||
plugin_name = os.path.basename(filename.replace(".py", ""))
|
||||
available[plugin_name] = filename
|
||||
return available
|
||||
|
||||
|
||||
def _get_installed(config):
|
||||
"""
|
||||
Get all installed plugins
|
||||
"""
|
||||
installed = dict()
|
||||
search_dirs = [ default_path, config['main']['custom_plugins'] ]
|
||||
for search_dir in search_dirs:
|
||||
if search_dir:
|
||||
for filename in glob.glob(os.path.join(search_dir, "*.py")):
|
||||
plugin_name = os.path.basename(filename.replace(".py", ""))
|
||||
installed[plugin_name] = filename
|
||||
return installed
|
||||
|
||||
|
||||
def uninstall(args, config):
|
||||
"""
|
||||
Uninstalls a plugin
|
||||
"""
|
||||
plugin_name = args.name
|
||||
installed = _get_installed(config)
|
||||
if plugin_name not in installed:
|
||||
logging.error('Plugin %s is not installed.', plugin_name)
|
||||
return 1
|
||||
os.remove(installed[plugin_name])
|
||||
return 0
|
||||
|
||||
|
||||
def install(args, config):
|
||||
"""
|
||||
Installs the given plugin
|
||||
"""
|
||||
global DEFAULT_INSTALL_PATH
|
||||
plugin_name = args.name
|
||||
available = _get_available()
|
||||
installed = _get_installed(config)
|
||||
|
||||
if plugin_name not in available:
|
||||
logging.error('%s not found.', plugin_name)
|
||||
return 1
|
||||
|
||||
if plugin_name in installed:
|
||||
logging.error('%s already installed.', plugin_name)
|
||||
|
||||
# install into custom_plugins path
|
||||
install_path = config['main']['custom_plugins']
|
||||
if not install_path:
|
||||
install_path = DEFAULT_INSTALL_PATH
|
||||
config['main']['custom_plugins'] = install_path
|
||||
save_config(config, args.user_config)
|
||||
|
||||
os.makedirs(install_path, exist_ok=True)
|
||||
|
||||
shutil.copyfile(available[plugin_name], os.path.join(install_path, os.path.basename(available[plugin_name])))
|
||||
|
||||
# maybe has config
|
||||
for conf in glob.glob(available[plugin_name].replace('.py', '.y?ml')):
|
||||
dst = os.path.join(install_path, os.path.basename(conf))
|
||||
if os.path.exists(dst) and md5(dst) != md5(conf):
|
||||
# backup
|
||||
logging.info('Backing up config: %s', os.path.basename(conf))
|
||||
shutil.move(dst, dst + '.bak')
|
||||
shutil.copyfile(conf, dst)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _analyse_dir(path):
|
||||
results = dict()
|
||||
path += '*' if path.endswith('/') else '/*'
|
||||
for filename in glob.glob(path, recursive=True):
|
||||
if not os.path.isfile(filename):
|
||||
continue
|
||||
try:
|
||||
results[filename] = md5(filename)
|
||||
except OSError:
|
||||
continue
|
||||
return results
|
||||
|
||||
|
||||
def update(config):
|
||||
"""
|
||||
Updates the database
|
||||
"""
|
||||
global SAVE_DIR
|
||||
|
||||
urls = config['main']['custom_plugin_repos']
|
||||
if not urls:
|
||||
logging.info('No plugin repositories configured.')
|
||||
return 1
|
||||
|
||||
rc = 0
|
||||
for idx, REPO_URL in enumerate(urls):
|
||||
DEST = os.path.join(SAVE_DIR, 'plugins%d.zip' % idx)
|
||||
logging.info('Downloading plugins from %s to %s', REPO_URL, DEST)
|
||||
|
||||
try:
|
||||
os.makedirs(SAVE_DIR, exist_ok=True)
|
||||
before_update = _analyse_dir(SAVE_DIR)
|
||||
|
||||
download_file(REPO_URL, os.path.join(SAVE_DIR, DEST))
|
||||
|
||||
logging.info('Unzipping...')
|
||||
unzip(DEST, SAVE_DIR, strip_dirs=1)
|
||||
|
||||
after_update = _analyse_dir(SAVE_DIR)
|
||||
|
||||
b_len = len(before_update)
|
||||
a_len = len(after_update)
|
||||
|
||||
if a_len > b_len:
|
||||
logging.info('Found %d new file(s).', a_len - b_len)
|
||||
|
||||
changed = 0
|
||||
for filename, filehash in after_update.items():
|
||||
if filename in before_update and filehash != before_update[filename]:
|
||||
changed += 1
|
||||
|
||||
if changed:
|
||||
logging.info('%d file(s) were changed.', changed)
|
||||
|
||||
except Exception as ex:
|
||||
logging.error('Error while updating plugins: %s', ex)
|
||||
rc = 1
|
||||
return rc
|
@@ -1,57 +0,0 @@
|
||||
import pwnagotchi.plugins as plugins
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import string
|
||||
import os
|
||||
|
||||
'''
|
||||
Aircrack-ng needed, to install:
|
||||
> apt-get install aircrack-ng
|
||||
'''
|
||||
|
||||
|
||||
class AircrackOnly(plugins.Plugin):
|
||||
__author__ = 'pwnagotchi [at] rossmarks [dot] uk'
|
||||
__version__ = '1.0.1'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'confirm pcap contains handshake/PMKID or delete it'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(self)
|
||||
self.text_to_set = ""
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("aircrackonly plugin loaded")
|
||||
|
||||
def on_handshake(self, agent, filename, access_point, client_station):
|
||||
display = agent._view
|
||||
todelete = 0
|
||||
handshakeFound = 0
|
||||
|
||||
result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "1 handshake" | awk \'{print $2}\''),
|
||||
shell=True, stdout=subprocess.PIPE)
|
||||
result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
|
||||
if result:
|
||||
handshakeFound = 1
|
||||
logging.info("[AircrackOnly] contains handshake")
|
||||
|
||||
if handshakeFound == 0:
|
||||
result = subprocess.run(('/usr/bin/aircrack-ng ' + filename + ' | grep "PMKID" | awk \'{print $2}\''),
|
||||
shell=True, stdout=subprocess.PIPE)
|
||||
result = result.stdout.decode('utf-8').translate({ord(c): None for c in string.whitespace})
|
||||
if result:
|
||||
logging.info("[AircrackOnly] contains PMKID")
|
||||
else:
|
||||
todelete = 1
|
||||
|
||||
if todelete == 1:
|
||||
os.remove(filename)
|
||||
self.text_to_set = "Removed an uncrackable pcap"
|
||||
display.update(force=True)
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if self.text_to_set:
|
||||
ui.set('face', "(>.<)")
|
||||
ui.set('status', self.text_to_set)
|
||||
self.text_to_set = ""
|
@@ -1,65 +0,0 @@
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.utils import StatusFile
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
class AutoBackup(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '1.0.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin backups files when internet is available.'
|
||||
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.tries = 0
|
||||
self.status = StatusFile('/root/.auto-backup')
|
||||
|
||||
def on_loaded(self):
|
||||
for opt in ['files', 'interval', 'commands', 'max_tries']:
|
||||
if opt not in self.options or (opt in self.options and self.options[opt] is None):
|
||||
logging.error(f"AUTO-BACKUP: Option {opt} is not set.")
|
||||
return
|
||||
|
||||
self.ready = True
|
||||
logging.info("AUTO-BACKUP: Successfully loaded.")
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
if not self.ready:
|
||||
return
|
||||
|
||||
if self.options['max_tries'] and self.tries >= self.options['max_tries']:
|
||||
return
|
||||
|
||||
if self.status.newer_then_days(self.options['interval']):
|
||||
return
|
||||
|
||||
# Only backup existing files to prevent errors
|
||||
existing_files = list(filter(lambda f: os.path.exists(f), self.options['files']))
|
||||
files_to_backup = " ".join(existing_files)
|
||||
|
||||
try:
|
||||
display = agent.view()
|
||||
|
||||
logging.info("AUTO-BACKUP: Backing up ...")
|
||||
display.set('status', 'Backing up ...')
|
||||
display.update()
|
||||
|
||||
for cmd in self.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")
|
||||
display.set('status', 'Backup done!')
|
||||
display.update()
|
||||
self.status.update()
|
||||
except OSError as os_e:
|
||||
self.tries += 1
|
||||
logging.info(f"AUTO-BACKUP: Error: {os_e}")
|
||||
display.set('status', 'Backup failed!')
|
||||
display.update()
|
@@ -6,11 +6,11 @@ import requests
|
||||
import platform
|
||||
import shutil
|
||||
import glob
|
||||
import pkg_resources
|
||||
from threading import Lock
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.utils import StatusFile
|
||||
from pwnagotchi.utils import StatusFile, parse_version as version_to_tuple
|
||||
|
||||
|
||||
def check(version, repo, native=True):
|
||||
@@ -29,8 +29,8 @@ def check(version, repo, native=True):
|
||||
info['available'] = latest_ver = latest['tag_name'].replace('v', '')
|
||||
is_arm = info['arch'].startswith('arm')
|
||||
|
||||
local = pkg_resources.parse_version(info['current'])
|
||||
remote = pkg_resources.parse_version(latest_ver)
|
||||
local = version_to_tuple(info['current'])
|
||||
remote = version_to_tuple(latest_ver)
|
||||
if remote > local:
|
||||
if not native:
|
||||
info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name'])
|
||||
@@ -150,68 +150,74 @@ class AutoUpdate(plugins.Plugin):
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.status = StatusFile('/root/.auto-update')
|
||||
self.lock = Lock()
|
||||
|
||||
def on_loaded(self):
|
||||
if 'interval' not in self.options or ('interval' in self.options and self.options['interval'] is None):
|
||||
if 'interval' not in self.options or ('interval' in self.options and not self.options['interval']):
|
||||
logging.error("[update] main.plugins.auto-update.interval is not set")
|
||||
return
|
||||
self.ready = True
|
||||
logging.info("[update] plugin loaded.")
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
logging.debug("[update] internet connectivity is available (ready %s)" % self.ready)
|
||||
|
||||
if not self.ready:
|
||||
if self.lock.locked():
|
||||
return
|
||||
|
||||
if self.status.newer_then_hours(self.options['interval']):
|
||||
logging.debug("[update] last check happened less than %d hours ago" % self.options['interval'])
|
||||
return
|
||||
with self.lock:
|
||||
logging.debug("[update] internet connectivity is available (ready %s)" % self.ready)
|
||||
|
||||
logging.info("[update] checking for updates ...")
|
||||
if not self.ready:
|
||||
return
|
||||
|
||||
display = agent.view()
|
||||
prev_status = display.get('status')
|
||||
if self.status.newer_then_hours(self.options['interval']):
|
||||
logging.debug("[update] last check happened less than %d hours ago" % self.options['interval'])
|
||||
return
|
||||
|
||||
try:
|
||||
display.update(force=True, new_data={'status': 'Checking for updates ...'})
|
||||
logging.info("[update] checking for updates ...")
|
||||
|
||||
to_install = []
|
||||
to_check = [
|
||||
('bettercap/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
|
||||
('evilsocket/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
|
||||
('evilsocket/pwnagotchi', pwnagotchi.version, False, 'pwnagotchi')
|
||||
]
|
||||
display = agent.view()
|
||||
prev_status = display.get('status')
|
||||
|
||||
for repo, local_version, is_native, svc_name in to_check:
|
||||
info = check(local_version, repo, is_native)
|
||||
if info['url'] is not None:
|
||||
logging.warning(
|
||||
"update for %s available (local version is '%s'): %s" % (
|
||||
repo, info['current'], info['url']))
|
||||
info['service'] = svc_name
|
||||
to_install.append(info)
|
||||
try:
|
||||
display.update(force=True, new_data={'status': 'Checking for updates ...'})
|
||||
|
||||
num_updates = len(to_install)
|
||||
num_installed = 0
|
||||
to_install = []
|
||||
to_check = [
|
||||
('bettercap/bettercap', parse_version('bettercap -version'), True, 'bettercap'),
|
||||
('evilsocket/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'),
|
||||
('evilsocket/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi')
|
||||
]
|
||||
|
||||
if num_updates > 0:
|
||||
if self.options['install']:
|
||||
for update in to_install:
|
||||
if install(display, update):
|
||||
num_installed += 1
|
||||
else:
|
||||
prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '')
|
||||
for repo, local_version, is_native, svc_name in to_check:
|
||||
info = check(local_version, repo, is_native)
|
||||
if info['url'] is not None:
|
||||
logging.warning(
|
||||
"update for %s available (local version is '%s'): %s" % (
|
||||
repo, info['current'], info['url']))
|
||||
info['service'] = svc_name
|
||||
to_install.append(info)
|
||||
|
||||
logging.info("[update] done")
|
||||
num_updates = len(to_install)
|
||||
num_installed = 0
|
||||
|
||||
self.status.update()
|
||||
if num_updates > 0:
|
||||
if self.options['install']:
|
||||
for update in to_install:
|
||||
plugins.on('updating')
|
||||
if install(display, update):
|
||||
num_installed += 1
|
||||
else:
|
||||
prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '')
|
||||
|
||||
if num_installed > 0:
|
||||
display.update(force=True, new_data={'status': 'Rebooting ...'})
|
||||
pwnagotchi.reboot()
|
||||
logging.info("[update] done")
|
||||
|
||||
except Exception as e:
|
||||
logging.error("[update] %s" % e)
|
||||
self.status.update()
|
||||
|
||||
display.update(force=True, new_data={'status': prev_status if prev_status is not None else ''})
|
||||
if num_installed > 0:
|
||||
display.update(force=True, new_data={'status': 'Rebooting ...'})
|
||||
pwnagotchi.reboot()
|
||||
|
||||
except Exception as e:
|
||||
logging.error("[update] %s" % e)
|
||||
|
||||
display.update(force=True, new_data={'status': prev_status if prev_status is not None else ''})
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from threading import Lock
|
||||
|
||||
import dbus
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.ui.components import LabeledValue
|
||||
from pwnagotchi.ui.view import BLACK
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.utils import StatusFile
|
||||
import pwnagotchi.plugins as plugins
|
||||
|
||||
|
||||
class BTError(Exception):
|
||||
@@ -382,7 +384,7 @@ class IfaceWrapper:
|
||||
|
||||
|
||||
class Device:
|
||||
def __init__(self, name, share_internet, mac, ip, netmask, interval, priority=10, scantime=15, search_order=0, max_tries=0, **kwargs):
|
||||
def __init__(self, name, share_internet, mac, ip, netmask, interval, gateway=None, priority=10, scantime=15, search_order=0, max_tries=0, **kwargs):
|
||||
self.name = name
|
||||
self.status = StatusFile(f'/root/.bt-tether-{name}')
|
||||
self.status.update()
|
||||
@@ -394,6 +396,7 @@ class Device:
|
||||
self.share_internet = share_internet
|
||||
self.ip = ip
|
||||
self.netmask = netmask
|
||||
self.gateway = gateway
|
||||
self.interval = interval
|
||||
self.mac = mac
|
||||
self.scantime = scantime
|
||||
@@ -416,14 +419,19 @@ class Device:
|
||||
|
||||
class BTTether(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '1.0.0'
|
||||
__version__ = '1.1.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This makes the display reachable over bluetooth'
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.options = dict()
|
||||
self.devices = dict()
|
||||
self.lock = Lock()
|
||||
self.running = True
|
||||
self.status = '-'
|
||||
|
||||
|
||||
def on_loaded(self):
|
||||
# new config
|
||||
@@ -433,7 +441,7 @@ class BTTether(plugins.Plugin):
|
||||
for device_opt in ['enabled', 'priority', 'scantime', 'search_order',
|
||||
'max_tries', 'share_internet', 'mac', 'ip',
|
||||
'netmask', 'interval']:
|
||||
if device_opt not in options or (device_opt in options and options[device_opt] is None):
|
||||
if device_opt not in options or options[device_opt] is None:
|
||||
logging.error("BT-TETHER: Please specify the %s for device %s.",
|
||||
device_opt, device)
|
||||
break
|
||||
@@ -444,8 +452,8 @@ class BTTether(plugins.Plugin):
|
||||
# legacy
|
||||
if 'mac' in self.options:
|
||||
for opt in ['share_internet', 'mac', 'ip', 'netmask', 'interval']:
|
||||
if opt not in self.options or (opt in self.options and self.options[opt] is None):
|
||||
logging.error("BT-TETHER: Please specify the %s in your config.yml.", opt)
|
||||
if opt not in self.options or self.options[opt] is None:
|
||||
logging.error("BT-TETHER: Please specify the %s in your config.toml.", opt)
|
||||
return
|
||||
|
||||
self.devices['legacy'] = Device(name='legacy', **self.options)
|
||||
@@ -461,110 +469,123 @@ class BTTether(plugins.Plugin):
|
||||
logging.error("BT-TETHER: Can't start bluetooth.service")
|
||||
return
|
||||
|
||||
logging.info("BT-TETHER: Sussessfully loaded ...")
|
||||
self.ready = True
|
||||
logging.info("BT-TETHER: Successfully loaded ...")
|
||||
|
||||
while self.running:
|
||||
time.sleep(1)
|
||||
|
||||
devices_to_try = list()
|
||||
connected_priorities = list()
|
||||
any_device_connected = False # if this is true, last status on screen should be C
|
||||
|
||||
for _, device in self.devices.items():
|
||||
if device.connected():
|
||||
connected_priorities.append(device.priority)
|
||||
any_device_connected = True
|
||||
continue
|
||||
|
||||
if not device.max_tries or (device.max_tries > device.tries):
|
||||
if not device.status.newer_then_minutes(device.interval):
|
||||
devices_to_try.append(device)
|
||||
device.status.update()
|
||||
device.tries += 1
|
||||
|
||||
sorted_devices = sorted(devices_to_try, key=lambda x: x.search_order)
|
||||
|
||||
for device in sorted_devices:
|
||||
bt = BTNap(device.mac)
|
||||
|
||||
try:
|
||||
logging.debug('BT-TETHER: Search %d secs for %s ...', device.scantime, device.name)
|
||||
dev_remote = bt.wait_for_device(timeout=device.scantime)
|
||||
if dev_remote is None:
|
||||
logging.debug('BT-TETHER: Could not find %s, try again in %d minutes.', device.name, device.interval)
|
||||
self.status = 'NF'
|
||||
continue
|
||||
except Exception as bt_ex:
|
||||
logging.error(bt_ex)
|
||||
self.status = 'NF'
|
||||
continue
|
||||
|
||||
paired = bt.is_paired()
|
||||
if not paired:
|
||||
if BTNap.pair(dev_remote):
|
||||
logging.debug('BT-TETHER: Paired with %s.', device.name)
|
||||
else:
|
||||
logging.debug('BT-TETHER: Pairing with %s failed ...', device.name)
|
||||
self.status = 'PE'
|
||||
continue
|
||||
else:
|
||||
logging.debug('BT-TETHER: Already paired.')
|
||||
|
||||
|
||||
logging.debug('BT-TETHER: Try to create nap connection with %s ...', device.name)
|
||||
device.network, success = BTNap.nap(dev_remote)
|
||||
interface = None
|
||||
|
||||
if success:
|
||||
try:
|
||||
interface = device.interface()
|
||||
except Exception:
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
continue
|
||||
|
||||
if interface is None:
|
||||
self.status = 'BE'
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
continue
|
||||
|
||||
logging.debug('BT-TETHER: Created interface (%s)', interface)
|
||||
self.status = 'C'
|
||||
any_device_connected = True
|
||||
device.tries = 0 # reset tries
|
||||
else:
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
self.status = 'NF'
|
||||
continue
|
||||
|
||||
addr = f"{device.ip}/{device.netmask}"
|
||||
if device.gateway:
|
||||
gateway = device.gateway
|
||||
else:
|
||||
gateway = ".".join(device.ip.split('.')[:-1] + ['1'])
|
||||
|
||||
wrapped_interface = IfaceWrapper(interface)
|
||||
logging.debug('BT-TETHER: Add ip to %s', interface)
|
||||
if not wrapped_interface.set_addr(addr):
|
||||
self.status = 'AE'
|
||||
logging.debug("BT-TETHER: Could not add ip to %s", interface)
|
||||
continue
|
||||
|
||||
if device.share_internet:
|
||||
if not connected_priorities or device.priority > max(connected_priorities):
|
||||
logging.debug('BT-TETHER: Set default route to %s via %s', gateway, interface)
|
||||
IfaceWrapper.set_route(gateway, interface)
|
||||
connected_priorities.append(device.priority)
|
||||
|
||||
logging.debug('BT-TETHER: Change resolv.conf if necessary ...')
|
||||
with open('/etc/resolv.conf', 'r+') as resolv:
|
||||
nameserver = resolv.read()
|
||||
if 'nameserver 9.9.9.9' not in nameserver:
|
||||
logging.debug('BT-TETHER: Added nameserver')
|
||||
resolv.seek(0)
|
||||
resolv.write(nameserver + 'nameserver 9.9.9.9\n')
|
||||
|
||||
if any_device_connected:
|
||||
self.status = 'C'
|
||||
|
||||
|
||||
def on_unload(self, ui):
|
||||
self.running = False
|
||||
with ui._lock:
|
||||
ui.remove_element('bluetooth')
|
||||
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
with ui._lock:
|
||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if not self.ready:
|
||||
return
|
||||
|
||||
devices_to_try = list()
|
||||
connected_priorities = list()
|
||||
any_device_connected = False # if this is true, last status on screen should be C
|
||||
|
||||
for _, device in self.devices.items():
|
||||
if device.connected():
|
||||
connected_priorities.append(device.priority)
|
||||
any_device_connected = True
|
||||
continue
|
||||
|
||||
if not device.max_tries or (device.max_tries > device.tries):
|
||||
if not device.status.newer_then_minutes(device.interval):
|
||||
devices_to_try.append(device)
|
||||
device.status.update()
|
||||
device.tries += 1
|
||||
|
||||
sorted_devices = sorted(devices_to_try, key=lambda x: x.search_order)
|
||||
|
||||
for device in sorted_devices:
|
||||
bt = BTNap(device.mac)
|
||||
|
||||
try:
|
||||
logging.debug('BT-TETHER: Search %d secs for %s ...', device.scantime, device.name)
|
||||
dev_remote = bt.wait_for_device(timeout=device.scantime)
|
||||
if dev_remote is None:
|
||||
logging.debug('BT-TETHER: Could not find %s, try again in %d minutes.', device.name, device.interval)
|
||||
ui.set('bluetooth', 'NF')
|
||||
continue
|
||||
except Exception as bt_ex:
|
||||
logging.error(bt_ex)
|
||||
ui.set('bluetooth', 'NF')
|
||||
continue
|
||||
|
||||
paired = bt.is_paired()
|
||||
if not paired:
|
||||
if BTNap.pair(dev_remote):
|
||||
logging.debug('BT-TETHER: Paired with %s.', device.name)
|
||||
else:
|
||||
logging.debug('BT-TETHER: Pairing with %s failed ...', device.name)
|
||||
ui.set('bluetooth', 'PE')
|
||||
continue
|
||||
else:
|
||||
logging.debug('BT-TETHER: Already paired.')
|
||||
|
||||
|
||||
logging.debug('BT-TETHER: Try to create nap connection with %s ...', device.name)
|
||||
device.network, success = BTNap.nap(dev_remote)
|
||||
interface = None
|
||||
|
||||
if success:
|
||||
try:
|
||||
interface = device.interface()
|
||||
except Exception:
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
continue
|
||||
|
||||
if interface is None:
|
||||
ui.set('bluetooth', 'BE')
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
continue
|
||||
|
||||
logging.debug('BT-TETHER: Created interface (%s)', interface)
|
||||
ui.set('bluetooth', 'C')
|
||||
any_device_connected = True
|
||||
device.tries = 0 # reset tries
|
||||
else:
|
||||
logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name)
|
||||
ui.set('bluetooth', 'NF')
|
||||
continue
|
||||
|
||||
addr = f"{device.ip}/{device.netmask}"
|
||||
gateway = ".".join(device.ip.split('.')[:-1] + ['1'])
|
||||
|
||||
wrapped_interface = IfaceWrapper(interface)
|
||||
logging.debug('BT-TETHER: Add ip to %s', interface)
|
||||
if not wrapped_interface.set_addr(addr):
|
||||
ui.set('bluetooth', 'AE')
|
||||
logging.debug("BT-TETHER: Could not add ip to %s", interface)
|
||||
continue
|
||||
|
||||
if device.share_internet:
|
||||
if not connected_priorities or device.priority > max(connected_priorities):
|
||||
logging.debug('BT-TETHER: Set default route to %s via %s', gateway, interface)
|
||||
IfaceWrapper.set_route(gateway, interface)
|
||||
connected_priorities.append(device.priority)
|
||||
|
||||
logging.debug('BT-TETHER: Change resolv.conf if necessary ...')
|
||||
with open('/etc/resolv.conf', 'r+') as resolv:
|
||||
nameserver = resolv.read()
|
||||
if 'nameserver 9.9.9.9' not in nameserver:
|
||||
logging.debug('BT-TETHER: Added nameserver')
|
||||
resolv.seek(0)
|
||||
resolv.write(nameserver + 'nameserver 9.9.9.9\n')
|
||||
|
||||
if any_device_connected:
|
||||
ui.set('bluetooth', 'C')
|
||||
ui.set('bluetooth', self.status)
|
||||
|
@@ -16,27 +16,20 @@ class Example(plugins.Plugin):
|
||||
logging.debug("example plugin created")
|
||||
|
||||
# called when http://<host>:<port>/plugins/<plugin>/ is called
|
||||
# must return a response
|
||||
def on_webhook(self, path, args, req_method):
|
||||
# must return a html page
|
||||
# IMPORTANT: If you use "POST"s, add a csrf-token (via csrf_token() and render_template_string)
|
||||
def on_webhook(self, path, request):
|
||||
pass
|
||||
|
||||
# called when the plugin is loaded
|
||||
def on_loaded(self):
|
||||
logging.warning("WARNING: this plugin should be disabled! options = " % self.options)
|
||||
|
||||
# called when <host>:<port>/plugins/<pluginname> is opened
|
||||
def on_webhook(self, response, path):
|
||||
res = "<html><body><a>Hook triggered</a></body></html>"
|
||||
response.send_response(200)
|
||||
response.send_header('Content-type', 'text/html')
|
||||
response.end_headers()
|
||||
# called before the plugin is unloaded
|
||||
def on_unload(self, ui):
|
||||
pass
|
||||
|
||||
try:
|
||||
response.wfile.write(bytes(res, "utf-8"))
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
|
||||
# called in manual mode when there's internet connectivity
|
||||
# called hen there's internet connectivity
|
||||
def on_internet_available(self, agent):
|
||||
pass
|
||||
|
||||
@@ -106,7 +99,7 @@ class Example(plugins.Plugin):
|
||||
pass
|
||||
|
||||
# called when the status is set to excited
|
||||
def on_excited(aself, gent):
|
||||
def on_excited(self, agent):
|
||||
pass
|
||||
|
||||
# called when the status is set to lonely
|
||||
@@ -129,6 +122,11 @@ class Example(plugins.Plugin):
|
||||
def on_wifi_update(self, agent, access_points):
|
||||
pass
|
||||
|
||||
# called when the agent refreshed an unfiltered access point list
|
||||
# this list contains all access points that were detected BEFORE filtering
|
||||
def on_unfiltered_ap_list(self, agent, access_points):
|
||||
pass
|
||||
|
||||
# called when the agent is sending an association frame
|
||||
def on_association(self, agent, access_point):
|
||||
pass
|
||||
|
@@ -31,10 +31,9 @@ class GPIOButtons(plugins.Plugin):
|
||||
# set gpio numbering
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
for i in gpios:
|
||||
gpio = list(i)[0]
|
||||
command = i[gpio]
|
||||
for gpio, command in gpios.items():
|
||||
gpio = int(gpio)
|
||||
self.ports[gpio] = command
|
||||
GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP)
|
||||
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=250)
|
||||
GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600)
|
||||
logging.info("Added command: %s to GPIO #%d", command, gpio)
|
||||
|
@@ -1,42 +1,160 @@
|
||||
import logging
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
from pwnagotchi.ui.components import LabeledValue
|
||||
from pwnagotchi.ui.view import BLACK
|
||||
|
||||
|
||||
class GPS(plugins.Plugin):
|
||||
__author__ = 'evilsocket@gmail.com'
|
||||
__version__ = '1.0.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'Save GPS coordinates whenever an handshake is captured.'
|
||||
__author__ = "evilsocket@gmail.com"
|
||||
__version__ = "1.0.1"
|
||||
__license__ = "GPL3"
|
||||
__description__ = "Save GPS coordinates whenever an handshake is captured."
|
||||
|
||||
LINE_SPACING = 10
|
||||
LABEL_SPACING = 0
|
||||
|
||||
def __init__(self):
|
||||
self.running = False
|
||||
self.coordinates = None
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("gps plugin loaded for %s" % self.options['device'])
|
||||
logging.info(f"gps plugin loaded for {self.options['device']}")
|
||||
|
||||
def on_ready(self, agent):
|
||||
if os.path.exists(self.options['device']):
|
||||
logging.info("enabling gps bettercap's module for %s" % self.options['device'])
|
||||
if os.path.exists(self.options["device"]):
|
||||
logging.info(
|
||||
f"enabling bettercap's gps module for {self.options['device']}"
|
||||
)
|
||||
try:
|
||||
agent.run('gps off')
|
||||
except:
|
||||
agent.run("gps off")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
agent.run('set gps.device %s' % self.options['device'])
|
||||
agent.run('set gps.speed %d' % self.options['speed'])
|
||||
agent.run('gps on')
|
||||
running = True
|
||||
agent.run(f"set gps.device {self.options['device']}")
|
||||
agent.run(f"set gps.baudrate {self.options['speed']}")
|
||||
agent.run("gps on")
|
||||
self.running = True
|
||||
else:
|
||||
logging.warning("no GPS detected")
|
||||
|
||||
def on_handshake(self, agent, filename, access_point, client_station):
|
||||
if self.running:
|
||||
info = agent.session()
|
||||
gps = info['gps']
|
||||
gps_filename = filename.replace('.pcap', '.gps.json')
|
||||
self.coordinates = info["gps"]
|
||||
gps_filename = filename.replace(".pcap", ".gps.json")
|
||||
|
||||
logging.info("saving GPS to %s (%s)" % (gps_filename, gps))
|
||||
with open(gps_filename, 'w+t') as fp:
|
||||
json.dump(gps, fp)
|
||||
if self.coordinates and all([
|
||||
# avoid 0.000... measurements
|
||||
self.coordinates["Latitude"], self.coordinates["Longitude"]
|
||||
]):
|
||||
logging.info(f"saving GPS to {gps_filename} ({self.coordinates})")
|
||||
with open(gps_filename, "w+t") as fp:
|
||||
json.dump(self.coordinates, fp)
|
||||
else:
|
||||
logging.info("not saving GPS. Couldn't find location.")
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
try:
|
||||
# Configure line_spacing
|
||||
line_spacing = int(self.options['linespacing'])
|
||||
except Exception:
|
||||
# Set default value
|
||||
line_spacing = self.LINE_SPACING
|
||||
|
||||
try:
|
||||
# Configure position
|
||||
pos = self.options['position'].split(',')
|
||||
pos = [int(x.strip()) for x in pos]
|
||||
lat_pos = (pos[0] + 5, pos[1])
|
||||
lon_pos = (pos[0], pos[1] + line_spacing)
|
||||
alt_pos = (pos[0] + 5, pos[1] + (2 * line_spacing))
|
||||
except Exception:
|
||||
# Set default value based on display type
|
||||
if ui.is_waveshare_v2():
|
||||
lat_pos = (127, 74)
|
||||
lon_pos = (122, 84)
|
||||
alt_pos = (127, 94)
|
||||
elif ui.is_waveshare_v1():
|
||||
lat_pos = (130, 70)
|
||||
lon_pos = (125, 80)
|
||||
alt_pos = (130, 90)
|
||||
elif ui.is_inky():
|
||||
lat_pos = (127, 60)
|
||||
lon_pos = (122, 70)
|
||||
alt_pos = (127, 80)
|
||||
elif ui.is_waveshare144lcd():
|
||||
# guessed values, add tested ones if you can
|
||||
lat_pos = (67, 73)
|
||||
lon_pos = (62, 83)
|
||||
alt_pos = (67, 93)
|
||||
elif ui.is_dfrobot_v2():
|
||||
lat_pos = (127, 74)
|
||||
lon_pos = (122, 84)
|
||||
alt_pos = (127, 94)
|
||||
elif ui.is_waveshare27inch():
|
||||
lat_pos = (6, 120)
|
||||
lon_pos = (1, 135)
|
||||
alt_pos = (6, 150)
|
||||
else:
|
||||
# guessed values, add tested ones if you can
|
||||
lat_pos = (127, 51)
|
||||
lon_pos = (122, 61)
|
||||
alt_pos = (127, 71)
|
||||
|
||||
ui.add_element(
|
||||
"latitude",
|
||||
LabeledValue(
|
||||
color=BLACK,
|
||||
label="lat:",
|
||||
value="-",
|
||||
position=lat_pos,
|
||||
label_font=fonts.Small,
|
||||
text_font=fonts.Small,
|
||||
label_spacing=self.LABEL_SPACING,
|
||||
),
|
||||
)
|
||||
ui.add_element(
|
||||
"longitude",
|
||||
LabeledValue(
|
||||
color=BLACK,
|
||||
label="long:",
|
||||
value="-",
|
||||
position=lon_pos,
|
||||
label_font=fonts.Small,
|
||||
text_font=fonts.Small,
|
||||
label_spacing=self.LABEL_SPACING,
|
||||
),
|
||||
)
|
||||
ui.add_element(
|
||||
"altitude",
|
||||
LabeledValue(
|
||||
color=BLACK,
|
||||
label="alt:",
|
||||
value="-",
|
||||
position=alt_pos,
|
||||
label_font=fonts.Small,
|
||||
text_font=fonts.Small,
|
||||
label_spacing=self.LABEL_SPACING,
|
||||
),
|
||||
)
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
ui.remove_element('latitude')
|
||||
ui.remove_element('longitude')
|
||||
ui.remove_element('altitude')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if self.coordinates and all([
|
||||
# avoid 0.000... measurements
|
||||
self.coordinates["Latitude"], self.coordinates["Longitude"]
|
||||
]):
|
||||
# last char is sometimes not completely drawn ¯\_(ツ)_/¯
|
||||
# using an ending-whitespace as workaround on each line
|
||||
ui.set("latitude", f"{self.coordinates['Latitude']:.4f} ")
|
||||
ui.set("longitude", f"{self.coordinates['Longitude']:.4f} ")
|
||||
ui.set("altitude", f"{self.coordinates['Altitude']:.1f}m ")
|
||||
|
@@ -7,6 +7,7 @@ import re
|
||||
import pwnagotchi.grid as grid
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap
|
||||
from threading import Lock
|
||||
|
||||
|
||||
def parse_pcap(filename):
|
||||
@@ -54,6 +55,7 @@ class Grid(plugins.Plugin):
|
||||
|
||||
self.unread_messages = 0
|
||||
self.total_messages = 0
|
||||
self.lock = Lock()
|
||||
|
||||
def is_excluded(self, what):
|
||||
for skip in self.options['exclude']:
|
||||
@@ -67,7 +69,8 @@ class Grid(plugins.Plugin):
|
||||
logging.info("grid plugin loaded.")
|
||||
|
||||
def set_reported(self, reported, net_id):
|
||||
reported.append(net_id)
|
||||
if net_id not in reported:
|
||||
reported.append(net_id)
|
||||
self.report.update(data={'reported': reported})
|
||||
|
||||
def check_inbox(self, agent):
|
||||
@@ -77,6 +80,7 @@ class Grid(plugins.Plugin):
|
||||
self.unread_messages = len([m for m in messages if m['seen_at'] is None])
|
||||
|
||||
if self.unread_messages:
|
||||
plugins.on('unread_inbox', self.unread_messages)
|
||||
logging.debug("[grid] unread:%d total:%d" % (self.unread_messages, self.total_messages))
|
||||
agent.view().on_unread_messages(self.unread_messages, self.total_messages)
|
||||
|
||||
@@ -120,21 +124,25 @@ class Grid(plugins.Plugin):
|
||||
def on_internet_available(self, agent):
|
||||
logging.debug("internet available")
|
||||
|
||||
try:
|
||||
grid.update_data(agent.last_session)
|
||||
except Exception as e:
|
||||
logging.error("error connecting to the pwngrid-peer service: %s" % e)
|
||||
logging.debug(e, exc_info=True)
|
||||
if self.lock.locked():
|
||||
return
|
||||
|
||||
try:
|
||||
self.check_inbox(agent)
|
||||
except Exception as e:
|
||||
logging.error("[grid] error while checking inbox: %s" % e)
|
||||
logging.debug(e, exc_info=True)
|
||||
with self.lock:
|
||||
try:
|
||||
grid.update_data(agent.last_session)
|
||||
except Exception as e:
|
||||
logging.error("error connecting to the pwngrid-peer service: %s" % e)
|
||||
logging.debug(e, exc_info=True)
|
||||
return
|
||||
|
||||
try:
|
||||
self.check_handshakes(agent)
|
||||
except Exception as e:
|
||||
logging.error("[grid] error while checking pcaps: %s" % e)
|
||||
logging.debug(e, exc_info=True)
|
||||
try:
|
||||
self.check_inbox(agent)
|
||||
except Exception as e:
|
||||
logging.error("[grid] error while checking inbox: %s" % e)
|
||||
logging.debug(e, exc_info=True)
|
||||
|
||||
try:
|
||||
self.check_handshakes(agent)
|
||||
except Exception as e:
|
||||
logging.error("[grid] error while checking pcaps: %s" % e)
|
||||
logging.debug(e, exc_info=True)
|
||||
|
159
pwnagotchi/plugins/default/led.py
Normal file
159
pwnagotchi/plugins/default/led.py
Normal file
@@ -0,0 +1,159 @@
|
||||
from threading import Event
|
||||
import _thread
|
||||
import logging
|
||||
import time
|
||||
|
||||
import pwnagotchi.plugins as plugins
|
||||
|
||||
|
||||
class Led(plugins.Plugin):
|
||||
__author__ = 'evilsocket@gmail.com'
|
||||
__version__ = '1.0.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin blinks the PWR led with different patterns depending on the event.'
|
||||
|
||||
def __init__(self):
|
||||
self._is_busy = False
|
||||
self._event = Event()
|
||||
self._event_name = None
|
||||
self._led_file = "/sys/class/leds/led0/brightness"
|
||||
self._delay = 200
|
||||
|
||||
# called when the plugin is loaded
|
||||
def on_loaded(self):
|
||||
self._led_file = "/sys/class/leds/led%d/brightness" % self.options['led']
|
||||
self._delay = int(self.options['delay'])
|
||||
|
||||
logging.info("[led] plugin loaded for %s" % self._led_file)
|
||||
self._on_event('loaded')
|
||||
_thread.start_new_thread(self._worker, ())
|
||||
|
||||
def _on_event(self, event):
|
||||
if not self._is_busy:
|
||||
self._event_name = event
|
||||
self._event.set()
|
||||
logging.debug("[led] event '%s' set", event)
|
||||
else:
|
||||
logging.debug("[led] skipping event '%s' because the worker is busy", event)
|
||||
|
||||
def _led(self, on):
|
||||
with open(self._led_file, 'wt') as fp:
|
||||
fp.write(str(on))
|
||||
|
||||
def _blink(self, pattern):
|
||||
logging.debug("[led] using pattern '%s' ..." % pattern)
|
||||
for c in pattern:
|
||||
if c == ' ':
|
||||
self._led(1)
|
||||
else:
|
||||
self._led(0)
|
||||
time.sleep(self._delay / 1000.0)
|
||||
# reset
|
||||
self._led(0)
|
||||
|
||||
def _worker(self):
|
||||
while True:
|
||||
self._event.wait()
|
||||
self._event.clear()
|
||||
self._is_busy = True
|
||||
|
||||
try:
|
||||
if self._event_name in self.options['patterns']:
|
||||
pattern = self.options['patterns'][self._event_name]
|
||||
self._blink(pattern)
|
||||
else:
|
||||
logging.debug("[led] no pattern defined for %s" % self._event_name)
|
||||
except Exception as e:
|
||||
logging.exception("[led] error while blinking")
|
||||
|
||||
finally:
|
||||
self._is_busy = False
|
||||
|
||||
# called when the unit is updating its software
|
||||
def on_updating(self):
|
||||
self._on_event('updating')
|
||||
|
||||
# called when there's one or more unread pwnmail messages
|
||||
def on_unread_inbox(self, num_unread):
|
||||
self._on_event('unread_inbox')
|
||||
|
||||
# called when there's internet connectivity
|
||||
def on_internet_available(self, agent):
|
||||
self._on_event('internet_available')
|
||||
|
||||
# called when everything is ready and the main loop is about to start
|
||||
def on_ready(self, agent):
|
||||
self._on_event('ready')
|
||||
|
||||
# called when the AI finished loading
|
||||
def on_ai_ready(self, agent):
|
||||
self._on_event('ai_ready')
|
||||
|
||||
# called when the AI starts training for a given number of epochs
|
||||
def on_ai_training_start(self, agent, epochs):
|
||||
self._on_event('ai_training_start')
|
||||
|
||||
# called when the AI got the best reward so far
|
||||
def on_ai_best_reward(self, agent, reward):
|
||||
self._on_event('ai_best_reward')
|
||||
|
||||
# called when the AI got the worst reward so far
|
||||
def on_ai_worst_reward(self, agent, reward):
|
||||
self._on_event('ai_worst_reward')
|
||||
|
||||
# called when the status is set to bored
|
||||
def on_bored(self, agent):
|
||||
self._on_event('bored')
|
||||
|
||||
# called when the status is set to sad
|
||||
def on_sad(self, agent):
|
||||
self._on_event('sad')
|
||||
|
||||
# called when the status is set to excited
|
||||
def on_excited(self, agent):
|
||||
self._on_event('excited')
|
||||
|
||||
# called when the status is set to lonely
|
||||
def on_lonely(self, agent):
|
||||
self._on_event('lonely')
|
||||
|
||||
# called when the agent is rebooting the board
|
||||
def on_rebooting(self, agent):
|
||||
self._on_event('rebooting')
|
||||
|
||||
# called when the agent is waiting for t seconds
|
||||
def on_wait(self, agent, t):
|
||||
self._on_event('wait')
|
||||
|
||||
# called when the agent is sleeping for t seconds
|
||||
def on_sleep(self, agent, t):
|
||||
self._on_event('sleep')
|
||||
|
||||
# called when the agent refreshed its access points list
|
||||
def on_wifi_update(self, agent, access_points):
|
||||
self._on_event('wifi_update')
|
||||
|
||||
# called when the agent is sending an association frame
|
||||
def on_association(self, agent, access_point):
|
||||
self._on_event('association')
|
||||
|
||||
# called when the agent is deauthenticating a client station from an AP
|
||||
def on_deauthentication(self, agent, access_point, client_station):
|
||||
self._on_event('deauthentication')
|
||||
|
||||
# called when a new handshake is captured, access_point and client_station are json objects
|
||||
# if the agent could match the BSSIDs to the current list, otherwise they are just the strings of the BSSIDs
|
||||
def on_handshake(self, agent, filename, access_point, client_station):
|
||||
self._on_event('handshake')
|
||||
|
||||
# called when an epoch is over (where an epoch is a single loop of the main algorithm)
|
||||
def on_epoch(self, agent, epoch, epoch_data):
|
||||
self._on_event('epoch')
|
||||
|
||||
# called when a new peer is detected
|
||||
def on_peer_detected(self, agent, peer):
|
||||
self._on_event('peer_detected')
|
||||
|
||||
# called when a known peer is lost
|
||||
def on_peer_lost(self, agent, peer):
|
||||
self._on_event('peer_lost')
|
273
pwnagotchi/plugins/default/logtail.py
Normal file
273
pwnagotchi/plugins/default/logtail.py
Normal file
@@ -0,0 +1,273 @@
|
||||
import os
|
||||
import logging
|
||||
import threading
|
||||
from itertools import islice
|
||||
from time import sleep
|
||||
from datetime import datetime,timedelta
|
||||
from pwnagotchi import plugins
|
||||
from pwnagotchi.utils import StatusFile
|
||||
from flask import render_template_string
|
||||
from flask import jsonify
|
||||
from flask import abort
|
||||
from flask import Response
|
||||
|
||||
|
||||
TEMPLATE = """
|
||||
{% extends "base.html" %}
|
||||
{% set active_page = "plugins" %}
|
||||
{% block title %}
|
||||
Logtail
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
{{ super() }}
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#filter {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
padding: 12px 20px 12px 40px;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 12px;
|
||||
width: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td:nth-child(2) {
|
||||
text-align: center;
|
||||
}
|
||||
thead, tr:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
tr {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
div.sticky {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
div.sticky > * {
|
||||
display: table-cell;
|
||||
}
|
||||
div.sticky > span {
|
||||
width: 1%;
|
||||
}
|
||||
div.sticky > input {
|
||||
width: 100%;
|
||||
}
|
||||
tr.default {
|
||||
color: black;
|
||||
}
|
||||
tr.info {
|
||||
color: black;
|
||||
}
|
||||
tr.warning {
|
||||
color: darkorange;
|
||||
}
|
||||
tr.error {
|
||||
color: crimson;
|
||||
}
|
||||
tr.debug {
|
||||
color: blueviolet;
|
||||
}
|
||||
.ui-mobile .ui-page-active {
|
||||
overflow: visible;
|
||||
overflow-x: visible;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
var table = document.getElementById('table');
|
||||
var filter = document.getElementById('filter');
|
||||
var filterVal = filter.value.toUpperCase();
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '{{ url_for('plugins') }}/logtail/stream');
|
||||
xhr.send();
|
||||
var position = 0;
|
||||
var data;
|
||||
var time;
|
||||
var level;
|
||||
var msg;
|
||||
var colorClass;
|
||||
|
||||
function handleNewData() {
|
||||
var messages = xhr.responseText.split('\\n');
|
||||
filterVal = filter.value.toUpperCase();
|
||||
messages.slice(position, -1).forEach(function(value) {
|
||||
|
||||
if (value.charAt(0) != '[') {
|
||||
msg = value;
|
||||
time = '';
|
||||
level = '';
|
||||
} else {
|
||||
data = value.split(']');
|
||||
time = data.shift() + ']';
|
||||
level = data.shift() + ']';
|
||||
msg = data.join(']');
|
||||
|
||||
switch(level) {
|
||||
case ' [INFO]':
|
||||
colorClass = 'info';
|
||||
break;
|
||||
case ' [WARNING]':
|
||||
colorClass = 'warning';
|
||||
break;
|
||||
case ' [ERROR]':
|
||||
colorClass = 'error';
|
||||
break;
|
||||
case ' [DEBUG]':
|
||||
colorClass = 'debug';
|
||||
break;
|
||||
default:
|
||||
colorClass = 'default';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var tr = document.createElement('tr');
|
||||
var td1 = document.createElement('td');
|
||||
var td2 = document.createElement('td');
|
||||
var td3 = document.createElement('td');
|
||||
|
||||
td1.textContent = time;
|
||||
td2.textContent = level;
|
||||
td3.textContent = msg;
|
||||
|
||||
tr.appendChild(td1);
|
||||
tr.appendChild(td2);
|
||||
tr.appendChild(td3);
|
||||
|
||||
tr.className = colorClass;
|
||||
|
||||
if (filterVal.length > 0 && value.toUpperCase().indexOf(filterVal) == -1) {
|
||||
tr.style.display = "none";
|
||||
}
|
||||
|
||||
table.appendChild(tr);
|
||||
});
|
||||
position = messages.length - 1;
|
||||
}
|
||||
|
||||
var scrollingElement = (document.scrollingElement || document.body)
|
||||
function scrollToBottom () {
|
||||
scrollingElement.scrollTop = scrollingElement.scrollHeight;
|
||||
}
|
||||
|
||||
var timer;
|
||||
var scrollElm = document.getElementById('autoscroll');
|
||||
timer = setInterval(function() {
|
||||
handleNewData();
|
||||
if (scrollElm.checked) {
|
||||
scrollToBottom();
|
||||
}
|
||||
if (xhr.readyState == XMLHttpRequest.DONE) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
var typingTimer;
|
||||
var doneTypingInterval = 1000;
|
||||
|
||||
filter.onkeyup = function() {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(doneTyping, doneTypingInterval);
|
||||
}
|
||||
|
||||
filter.onkeydown = function() {
|
||||
clearTimeout(typingTimer);
|
||||
}
|
||||
|
||||
function doneTyping() {
|
||||
document.body.style.cursor = 'progress';
|
||||
var tr, tds, td, i, txtValue;
|
||||
filterVal = filter.value.toUpperCase();
|
||||
tr = table.getElementsByTagName("tr");
|
||||
for (i = 1; i < tr.length; i++) {
|
||||
txtValue = tr[i].textContent || tr[i].innerText;
|
||||
if (txtValue.toUpperCase().indexOf(filterVal) > -1) {
|
||||
tr[i].style.display = "table-row";
|
||||
} else {
|
||||
tr[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
document.body.style.cursor = 'default';
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="sticky">
|
||||
<input type="text" id="filter" placeholder="Search for ..." title="Type in a filter">
|
||||
<span><input checked type="checkbox" id="autoscroll"></span>
|
||||
<span><label for="autoscroll"> Autoscroll to bottom</label><br></span>
|
||||
</div>
|
||||
<table id="table">
|
||||
<thead>
|
||||
<th>
|
||||
Time
|
||||
</th>
|
||||
<th>
|
||||
Level
|
||||
</th>
|
||||
<th>
|
||||
Message
|
||||
</th>
|
||||
</thead>
|
||||
</table>
|
||||
{% endblock %}
|
||||
"""
|
||||
|
||||
|
||||
class Logtail(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '0.1.0'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin tails the logfile.'
|
||||
|
||||
def __init__(self):
|
||||
self.lock = threading.Lock()
|
||||
self.options = dict()
|
||||
self.ready = False
|
||||
|
||||
def on_config_changed(self, config):
|
||||
self.config = config
|
||||
self.ready = True
|
||||
|
||||
def on_loaded(self):
|
||||
"""
|
||||
Gets called when the plugin gets loaded
|
||||
"""
|
||||
logging.info("Logtail plugin loaded.")
|
||||
|
||||
|
||||
def on_webhook(self, path, request):
|
||||
if not self.ready:
|
||||
return "Plugin not ready"
|
||||
|
||||
if not path or path == "/":
|
||||
return render_template_string(TEMPLATE)
|
||||
|
||||
if path == 'stream':
|
||||
def generate():
|
||||
with open(self.config['main']['log']['path']) as f:
|
||||
yield ''.join(f.readlines()[-self.options.get('max-lines', 4096):])
|
||||
while True:
|
||||
yield f.readline()
|
||||
|
||||
return Response(generate(), mimetype='text/plain')
|
||||
|
||||
abort(404)
|
@@ -1,6 +1,6 @@
|
||||
# memtemp shows memory infos and cpu temperature
|
||||
#
|
||||
# mem usage, cpu load, cpu temp
|
||||
# mem usage, cpu load, cpu temp, cpu frequency
|
||||
#
|
||||
###############################################################
|
||||
#
|
||||
@@ -16,8 +16,15 @@
|
||||
# - Added CPU load
|
||||
# - Added horizontal and vertical orientation
|
||||
#
|
||||
# 19-09-2020 by crahan <crahan@n00.be>
|
||||
# - Added CPU frequency
|
||||
# - Made field types and order configurable (max 3 fields)
|
||||
# - Made line spacing and position configurable
|
||||
# - Updated code to dynamically generate UI elements
|
||||
# - Changed horizontal UI elements to Text
|
||||
# - Updated to version 1.0.2
|
||||
###############################################################
|
||||
from pwnagotchi.ui.components import LabeledValue
|
||||
from pwnagotchi.ui.components import LabeledValue, Text
|
||||
from pwnagotchi.ui.view import BLACK
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
import pwnagotchi.plugins as plugins
|
||||
@@ -27,41 +34,151 @@ import logging
|
||||
|
||||
class MemTemp(plugins.Plugin):
|
||||
__author__ = 'https://github.com/xenDE'
|
||||
__version__ = '1.0.1'
|
||||
__version__ = '1.0.2'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'A plugin that will display memory/cpu usage and temperature'
|
||||
|
||||
ALLOWED_FIELDS = {
|
||||
'mem': 'mem_usage',
|
||||
'cpu': 'cpu_load',
|
||||
'temp': 'cpu_temp',
|
||||
'freq': 'cpu_freq'
|
||||
}
|
||||
DEFAULT_FIELDS = ['mem', 'cpu', 'temp']
|
||||
LINE_SPACING = 10
|
||||
LABEL_SPACING = 0
|
||||
FIELD_WIDTH = 4
|
||||
|
||||
def on_loaded(self):
|
||||
logging.info("memtemp plugin loaded.")
|
||||
|
||||
def mem_usage(self):
|
||||
return int(pwnagotchi.mem_usage() * 100)
|
||||
return f"{int(pwnagotchi.mem_usage() * 100)}%"
|
||||
|
||||
def cpu_load(self):
|
||||
return int(pwnagotchi.cpu_load() * 100)
|
||||
return f"{int(pwnagotchi.cpu_load() * 100)}%"
|
||||
|
||||
def cpu_temp(self):
|
||||
if self.options['scale'] == "fahrenheit":
|
||||
temp = (pwnagotchi.temperature() * 9 / 5) + 32
|
||||
symbol = "f"
|
||||
elif self.options['scale'] == "kelvin":
|
||||
temp = pwnagotchi.temperature() + 273.15
|
||||
symbol = "k"
|
||||
else:
|
||||
# default to celsius
|
||||
temp = pwnagotchi.temperature()
|
||||
symbol = "c"
|
||||
return f"{temp}{symbol}"
|
||||
|
||||
def cpu_freq(self):
|
||||
with open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', 'rt') as fp:
|
||||
return f"{round(float(fp.readline())/1000000, 1)}G"
|
||||
|
||||
def pad_text(self, data):
|
||||
return " " * (self.FIELD_WIDTH - len(data)) + data
|
||||
|
||||
def on_ui_setup(self, ui):
|
||||
if ui.is_waveshare_v2():
|
||||
h_pos = (180, 80)
|
||||
v_pos = (180, 61)
|
||||
else:
|
||||
h_pos = (155, 76)
|
||||
v_pos = (180, 61)
|
||||
try:
|
||||
# Configure field list
|
||||
self.fields = self.options['fields'].split(',')
|
||||
self.fields = [x.strip() for x in self.fields if x.strip() in self.ALLOWED_FIELDS.keys()]
|
||||
self.fields = self.fields[:3] # limit to the first 3 fields
|
||||
except Exception:
|
||||
# Set default value
|
||||
self.fields = self.DEFAULT_FIELDS
|
||||
|
||||
if self.options['orientation'] == "horizontal":
|
||||
ui.add_element('memtemp', LabeledValue(color=BLACK, label='', value='mem cpu temp\n - - -',
|
||||
position=h_pos,
|
||||
label_font=fonts.Small, text_font=fonts.Small))
|
||||
elif self.options['orientation'] == "vertical":
|
||||
ui.add_element('memtemp', LabeledValue(color=BLACK, label='', value=' mem:-\n cpu:-\ntemp:-',
|
||||
position=v_pos,
|
||||
label_font=fonts.Small, text_font=fonts.Small))
|
||||
try:
|
||||
# Configure line_spacing
|
||||
line_spacing = int(self.options['linespacing'])
|
||||
except Exception:
|
||||
# Set default value
|
||||
line_spacing = self.LINE_SPACING
|
||||
|
||||
try:
|
||||
# Configure position
|
||||
pos = self.options['position'].split(',')
|
||||
pos = [int(x.strip()) for x in pos]
|
||||
if self.options['orientation'] == "vertical":
|
||||
v_pos = (pos[0], pos[1])
|
||||
else:
|
||||
h_pos = (pos[0], pos[1])
|
||||
except Exception:
|
||||
# Set default position based on screen type
|
||||
if ui.is_waveshare_v2():
|
||||
h_pos = (178, 84)
|
||||
v_pos = (197, 74)
|
||||
elif ui.is_waveshare_v1():
|
||||
h_pos = (170, 80)
|
||||
v_pos = (165, 61)
|
||||
elif ui.is_waveshare144lcd():
|
||||
h_pos = (53, 77)
|
||||
v_pos = (73, 67)
|
||||
elif ui.is_inky():
|
||||
h_pos = (140, 68)
|
||||
v_pos = (160, 54)
|
||||
elif ui.is_waveshare27inch():
|
||||
h_pos = (192, 138)
|
||||
v_pos = (211, 122)
|
||||
else:
|
||||
h_pos = (155, 76)
|
||||
v_pos = (175, 61)
|
||||
|
||||
if self.options['orientation'] == "vertical":
|
||||
# Dynamically create the required LabeledValue objects
|
||||
for idx, field in enumerate(self.fields):
|
||||
v_pos_x = v_pos[0]
|
||||
v_pos_y = v_pos[1] + ((len(self.fields) - 3) * -1 * line_spacing)
|
||||
ui.add_element(
|
||||
f"memtemp_{field}",
|
||||
LabeledValue(
|
||||
color=BLACK,
|
||||
label=f"{self.pad_text(field)}:",
|
||||
value="-",
|
||||
position=(v_pos_x, v_pos_y + (idx * line_spacing)),
|
||||
label_font=fonts.Small,
|
||||
text_font=fonts.Small,
|
||||
label_spacing=self.LABEL_SPACING,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# default to horizontal
|
||||
h_pos_x = h_pos[0] + ((len(self.fields) - 3) * -1 * 25)
|
||||
h_pos_y = h_pos[1]
|
||||
ui.add_element(
|
||||
'memtemp_header',
|
||||
Text(
|
||||
color=BLACK,
|
||||
value=" ".join([self.pad_text(x) for x in self.fields]),
|
||||
position=(h_pos_x, h_pos_y),
|
||||
font=fonts.Small,
|
||||
)
|
||||
)
|
||||
ui.add_element(
|
||||
'memtemp_data',
|
||||
Text(
|
||||
color=BLACK,
|
||||
value=" ".join([self.pad_text("-") for x in self.fields]),
|
||||
position=(h_pos_x, h_pos_y + line_spacing),
|
||||
font=fonts.Small,
|
||||
)
|
||||
)
|
||||
|
||||
def on_unload(self, ui):
|
||||
with ui._lock:
|
||||
if self.options['orientation'] == "vertical":
|
||||
for idx, field in enumerate(self.fields):
|
||||
ui.remove_element(f"memtemp_{field}")
|
||||
else:
|
||||
# default to horizontal
|
||||
ui.remove_element('memtemp_header')
|
||||
ui.remove_element('memtemp_data')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
if self.options['orientation'] == "horizontal":
|
||||
ui.set('memtemp',
|
||||
" mem cpu temp\n %s%% %s%% %sc" % (self.mem_usage(), self.cpu_load(), pwnagotchi.temperature()))
|
||||
|
||||
elif self.options['orientation'] == "vertical":
|
||||
ui.set('memtemp',
|
||||
" mem:%s%%\n cpu:%s%%\ntemp:%sc" % (self.mem_usage(), self.cpu_load(), pwnagotchi.temperature()))
|
||||
if self.options['orientation'] == "vertical":
|
||||
for idx, field in enumerate(self.fields):
|
||||
ui.set(f"memtemp_{field}", getattr(self, self.ALLOWED_FIELDS[field])())
|
||||
else:
|
||||
# default to horizontal
|
||||
data = " ".join([self.pad_text(getattr(self, self.ALLOWED_FIELDS[x])()) for x in self.fields])
|
||||
ui.set('memtemp_data', data)
|
||||
|
@@ -1,35 +1,39 @@
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import requests
|
||||
import time
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi.utils import StatusFile
|
||||
|
||||
MOZILLA_API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}'
|
||||
|
||||
|
||||
class NetPos(plugins.Plugin):
|
||||
__author__ = 'zenzen san'
|
||||
__version__ = '2.0.1'
|
||||
__version__ = '2.0.3'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = """Saves a json file with the access points with more signal
|
||||
whenever a handshake is captured.
|
||||
When internet is available the files are converted in geo locations
|
||||
using Mozilla LocationService """
|
||||
|
||||
API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}'
|
||||
|
||||
def __init__(self):
|
||||
self.report = StatusFile('/root/.net_pos_saved', data_format='json')
|
||||
self.skip = list()
|
||||
self.ready = False
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def on_loaded(self):
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
|
||||
logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.")
|
||||
return
|
||||
|
||||
if 'api_url' in self.options:
|
||||
self.API_URL = self.options['api_url']
|
||||
self.ready = True
|
||||
logging.info("net-pos plugin loaded.")
|
||||
logging.debug(f"net-pos: use api_url: {self.API_URL}");
|
||||
|
||||
def _append_saved(self, path):
|
||||
to_save = list()
|
||||
@@ -45,60 +49,66 @@ class NetPos(plugins.Plugin):
|
||||
saved_file.write(x + "\n")
|
||||
|
||||
def on_internet_available(self, agent):
|
||||
if self.ready:
|
||||
config = agent.config()
|
||||
display = agent.view()
|
||||
reported = self.report.data_field_or('reported', default=list())
|
||||
handshake_dir = config['bettercap']['handshakes']
|
||||
if self.lock.locked():
|
||||
return
|
||||
with self.lock:
|
||||
if self.ready:
|
||||
config = agent.config()
|
||||
display = agent.view()
|
||||
reported = self.report.data_field_or('reported', default=list())
|
||||
handshake_dir = config['bettercap']['handshakes']
|
||||
|
||||
all_files = os.listdir(handshake_dir)
|
||||
all_np_files = [os.path.join(handshake_dir, filename)
|
||||
for filename in all_files
|
||||
if filename.endswith('.net-pos.json')]
|
||||
new_np_files = set(all_np_files) - set(reported) - set(self.skip)
|
||||
all_files = os.listdir(handshake_dir)
|
||||
all_np_files = [os.path.join(handshake_dir, filename)
|
||||
for filename in all_files
|
||||
if filename.endswith('.net-pos.json')]
|
||||
new_np_files = set(all_np_files) - set(reported) - set(self.skip)
|
||||
|
||||
if new_np_files:
|
||||
logging.info("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files))
|
||||
display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...")
|
||||
display.update(force=True)
|
||||
for idx, np_file in enumerate(new_np_files):
|
||||
if new_np_files:
|
||||
logging.debug("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files))
|
||||
display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...")
|
||||
display.update(force=True)
|
||||
for idx, np_file in enumerate(new_np_files):
|
||||
|
||||
geo_file = np_file.replace('.net-pos.json', '.geo.json')
|
||||
if os.path.exists(geo_file):
|
||||
# got already the position
|
||||
reported.append(np_file)
|
||||
self.report.update(data={'reported': reported})
|
||||
continue
|
||||
|
||||
try:
|
||||
geo_data = self._get_geo_data(np_file) # returns json obj
|
||||
except requests.exceptions.RequestException as req_e:
|
||||
logging.error("NET-POS: %s - RequestException: %s", np_file, req_e)
|
||||
self.skip += np_file
|
||||
continue
|
||||
except json.JSONDecodeError as js_e:
|
||||
logging.error("NET-POS: %s - JSONDecodeError: %s, removing it...", np_file, js_e)
|
||||
os.remove(np_file)
|
||||
continue
|
||||
except OSError as os_e:
|
||||
logging.error("NET-POS: %s - OSError: %s", np_file, os_e)
|
||||
self.skip += np_file
|
||||
continue
|
||||
|
||||
with open(geo_file, 'w+t') as sf:
|
||||
json.dump(geo_data, sf)
|
||||
|
||||
geo_file = np_file.replace('.net-pos.json', '.geo.json')
|
||||
if os.path.exists(geo_file):
|
||||
# got already the position
|
||||
reported.append(np_file)
|
||||
self.report.update(data={'reported': reported})
|
||||
continue
|
||||
|
||||
try:
|
||||
geo_data = self._get_geo_data(np_file) # returns json obj
|
||||
except requests.exceptions.RequestException as req_e:
|
||||
logging.error("NET-POS: %s", req_e)
|
||||
self.skip += np_file
|
||||
continue
|
||||
except json.JSONDecodeError as js_e:
|
||||
logging.error("NET-POS: %s", js_e)
|
||||
self.skip += np_file
|
||||
continue
|
||||
except OSError as os_e:
|
||||
logging.error("NET-POS: %s", os_e)
|
||||
self.skip += np_file
|
||||
continue
|
||||
|
||||
with open(geo_file, 'w+t') as sf:
|
||||
json.dump(geo_data, sf)
|
||||
|
||||
reported.append(np_file)
|
||||
self.report.update(data={'reported': reported})
|
||||
|
||||
display.set('status', f"Fetching positions ({idx + 1}/{len(new_np_files)})")
|
||||
display.update(force=True)
|
||||
display.set('status', f"Fetching positions ({idx + 1}/{len(new_np_files)})")
|
||||
display.update(force=True)
|
||||
|
||||
def on_handshake(self, agent, filename, access_point, client_station):
|
||||
netpos = self._get_netpos(agent)
|
||||
if not netpos['wifiAccessPoints']:
|
||||
return
|
||||
|
||||
netpos["ts"] = int("%.0f" % time.time())
|
||||
netpos_filename = filename.replace('.pcap', '.net-pos.json')
|
||||
logging.info("NET-POS: Saving net-location to %s", netpos_filename)
|
||||
logging.debug("NET-POS: Saving net-location to %s", netpos_filename)
|
||||
|
||||
try:
|
||||
with open(netpos_filename, 'w+t') as net_pos_file:
|
||||
@@ -106,6 +116,7 @@ class NetPos(plugins.Plugin):
|
||||
except OSError as os_e:
|
||||
logging.error("NET-POS: %s", os_e)
|
||||
|
||||
|
||||
def _get_netpos(self, agent):
|
||||
aps = agent.get_access_points()
|
||||
netpos = dict()
|
||||
@@ -117,7 +128,7 @@ class NetPos(plugins.Plugin):
|
||||
return netpos
|
||||
|
||||
def _get_geo_data(self, path, timeout=30):
|
||||
geourl = MOZILLA_API_URL.format(api=self.options['api_key'])
|
||||
geourl = self.API_URL.format(api=self.options['api_key'])
|
||||
|
||||
try:
|
||||
with open(path, "r") as json_file:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user