]> PatchyGit - angel.git/commitdiff
Initial commit
authorKylie Czar <czar@kalli.st>
Sun, 16 May 2021 21:02:27 +0000 (18:02 -0300)
committerKylie Czar <czar@kalli.st>
Sun, 16 May 2021 21:04:47 +0000 (18:04 -0300)
Pipfile [new file with mode: 0644]
Pipfile.lock [new file with mode: 0644]
README.org [new file with mode: 0644]
angel.png [new file with mode: 0644]
config.ini.default [new file with mode: 0644]
main.py [new file with mode: 0644]
requirements.txt [new file with mode: 0644]

diff --git a/Pipfile b/Pipfile
new file mode 100644 (file)
index 0000000..2d1c6e6
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,18 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+aiohttp = "*"
+requests = "*"
+slixmpp = "*"
+beautifulsoup4 = "*"
+pythonsed = {git = "https://github.com/GillesArcas/PythonSed"}
+pantomime = "*"
+youtube_dl = "*"
+
+[dev-packages]
+
+[requires]
+python_version = "3.9"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644 (file)
index 0000000..e20acbd
--- /dev/null
@@ -0,0 +1,417 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "26e57bb4727598eb003f5c3bd3edb44256cb9f1e6067f5bab09a48ee6e168300"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.9"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "aiodns": {
+            "hashes": [
+                "sha256:2b19bc5f97e5c936638d28e665923c093d8af2bf3aa88d35c43417fa25d136a2",
+                "sha256:946bdfabe743fceeeb093c8a010f5d1645f708a241be849e17edfb0e49e08cd6"
+            ],
+            "version": "==3.0.0"
+        },
+        "aiohttp": {
+            "hashes": [
+                "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe",
+                "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe",
+                "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5",
+                "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8",
+                "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd",
+                "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb",
+                "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c",
+                "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87",
+                "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0",
+                "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290",
+                "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5",
+                "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287",
+                "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde",
+                "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf",
+                "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8",
+                "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16",
+                "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf",
+                "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809",
+                "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213",
+                "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f",
+                "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013",
+                "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b",
+                "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9",
+                "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5",
+                "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb",
+                "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df",
+                "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4",
+                "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439",
+                "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f",
+                "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22",
+                "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f",
+                "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5",
+                "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970",
+                "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009",
+                "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc",
+                "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a",
+                "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"
+            ],
+            "index": "pypi",
+            "version": "==3.7.4.post0"
+        },
+        "async-timeout": {
+            "hashes": [
+                "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
+                "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
+            ],
+            "markers": "python_full_version >= '3.5.3'",
+            "version": "==3.0.1"
+        },
+        "attrs": {
+            "hashes": [
+                "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
+                "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+            "version": "==21.2.0"
+        },
+        "banal": {
+            "hashes": [
+                "sha256:2fe02c9305f53168441948f4a03dfbfa2eacc73db30db4a93309083cb0e250a5",
+                "sha256:877aacb16b17f8fa4fd29a7c44515c5a23dc1a7b26078bc41dd34829117d85e1"
+            ],
+            "version": "==1.0.6"
+        },
+        "beautifulsoup4": {
+            "hashes": [
+                "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35",
+                "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25",
+                "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"
+            ],
+            "index": "pypi",
+            "version": "==4.9.3"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
+                "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
+            ],
+            "version": "==2020.12.5"
+        },
+        "cffi": {
+            "hashes": [
+                "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813",
+                "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06",
+                "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea",
+                "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee",
+                "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396",
+                "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73",
+                "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315",
+                "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1",
+                "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49",
+                "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892",
+                "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482",
+                "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058",
+                "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5",
+                "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53",
+                "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045",
+                "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3",
+                "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5",
+                "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e",
+                "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c",
+                "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369",
+                "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827",
+                "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053",
+                "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa",
+                "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4",
+                "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322",
+                "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132",
+                "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62",
+                "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa",
+                "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0",
+                "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396",
+                "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e",
+                "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991",
+                "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6",
+                "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1",
+                "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406",
+                "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d",
+                "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
+            ],
+            "version": "==1.14.5"
+        },
+        "chardet": {
+            "hashes": [
+                "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
+                "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+            "version": "==4.0.0"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+                "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==2.10"
+        },
+        "multidict": {
+            "hashes": [
+                "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a",
+                "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93",
+                "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632",
+                "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656",
+                "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79",
+                "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7",
+                "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d",
+                "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5",
+                "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224",
+                "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26",
+                "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea",
+                "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348",
+                "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6",
+                "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76",
+                "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1",
+                "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f",
+                "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952",
+                "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a",
+                "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37",
+                "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9",
+                "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359",
+                "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8",
+                "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da",
+                "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3",
+                "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d",
+                "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf",
+                "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841",
+                "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d",
+                "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93",
+                "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f",
+                "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647",
+                "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635",
+                "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456",
+                "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda",
+                "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5",
+                "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
+                "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==5.1.0"
+        },
+        "normality": {
+            "hashes": [
+                "sha256:17c06fb63aa5f892eaca7fc25b93e91008b61ff64bb6ad7bd6320368f4f3fafa",
+                "sha256:513d1a394507cc0b132f14c08349d58f9ed24f583aeb1b26a93066a8230906de"
+            ],
+            "version": "==2.2.2"
+        },
+        "pantomime": {
+            "hashes": [
+                "sha256:0be4ebad9a4da6de32a6d3976697b8ad088a84ea229d50019898ed3dd5dea215",
+                "sha256:6e5628bfb166fd6ce617174fcaebb36265419149c5a4d3c716548ed22519fe27"
+            ],
+            "index": "pypi",
+            "version": "==0.4.1"
+        },
+        "pyasn1": {
+            "hashes": [
+                "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
+                "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
+                "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
+                "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
+                "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
+                "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
+                "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
+                "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
+                "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
+                "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
+                "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
+                "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
+                "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
+            ],
+            "version": "==0.4.8"
+        },
+        "pyasn1-modules": {
+            "hashes": [
+                "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
+                "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
+                "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
+                "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
+                "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
+                "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
+                "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
+                "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
+                "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
+                "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
+                "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
+                "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
+                "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
+            ],
+            "version": "==0.2.8"
+        },
+        "pycares": {
+            "hashes": [
+                "sha256:09b28fc7bc2cc05f7f69bf1636ddf46086e0a1837b62961e2092fcb40477320d",
+                "sha256:0aa97f900a7ffb259be77d640006585e2a907b0cd4edeee0e85cf16605995d5a",
+                "sha256:103353577a6266a53e71bfee4cf83825f1401fefa60f0fb8bdec35f13be6a5f2",
+                "sha256:1b959dd5921d207d759d421eece1b60416df33a7f862465739d5f2c363c2f523",
+                "sha256:26e67e4f81c80a5955dcf6193f3d9bee3c491fc0056299b383b84d792252fba4",
+                "sha256:38e54037f36c149146ff15f17a4a963fbdd0f9871d4a21cd94ff9f368140f57e",
+                "sha256:3c7fb8d34ee11971c39acfaf98d0fac66725385ccef3bfe1b174c92b210e1aa4",
+                "sha256:3d5e50c95849f6905d2a9dbf02ed03f82580173e3c5604a39e2ad054185631f1",
+                "sha256:44896d6e191a6b5a914dbe3aa7c748481bf6ad19a9df33c1e76f8f2dc33fc8f0",
+                "sha256:4876fc790ae32832ae270c4a010a1a77e12ddf8d8e6ad70ad0b0a9d506c985f7",
+                "sha256:53bc4f181b19576499b02cea4b45391e8dcbe30abd4cd01492f66bfc15615a13",
+                "sha256:57315b8eb8fdbc56b3ad4932bc4b17132bb7c7fd2bd590f7fb84b6b522098aa9",
+                "sha256:615406013cdcd1b445e5d1a551d276c6200b3abe77e534f8a7f7e1551208d14f",
+                "sha256:6580aef5d1b29a88c3d72fe73c691eacfd454f86e74d3fdd18f4bad8e8def98b",
+                "sha256:6f258c1b74c048a9501a25f732f11b401564005e5e3c18f1ca6cad0c3dc0fb19",
+                "sha256:7661d6bbd51a337e7373cb356efa8be9b4655fda484e068f9455e939aec8d54e",
+                "sha256:82b3259cb590ddd107a6d2dc52da2a2e9a986bf242e893d58c786af2f8191047",
+                "sha256:8ebb3ba0485f66cae8eed7ce3e9ed6f2c0bfd5e7319d5d0fbbb511064f17e1d4",
+                "sha256:a34b0e3e693dceb60b8a1169668d606c75cb100ceba0a2df53c234a0eb067fbc",
+                "sha256:ad6caf580ee69806fc6534be93ddbb6e99bf94296d79ab351c37b2992b17abfd",
+                "sha256:b17ef48729786e62b574c6431f675f4cb02b27691b49e7428a605a50cd59c072",
+                "sha256:c5362b7690ca481440f6b98395ac6df06aa50518ccb183c560464d1e5e2ab5d4",
+                "sha256:c95c964d5dd307e104b44b193095c67bb6b10c9eda1ffe7d44ab7a9e84c476d9",
+                "sha256:cd3011ffd5e1ad55880f7256791dbab9c43ebeda260474a968f19cd0319e1aef",
+                "sha256:d0154fc5753b088758fbec9bc137e1b24bb84fc0c6a09725c8bac25a342311cd",
+                "sha256:d4a5081e232c1d181883dcac4675807f3a6cf33911c4173fbea00c0523687ed4",
+                "sha256:d52f9c725d2a826d5ffa37681eb07ffb996bfe21788590ef257664a3898fc0b5",
+                "sha256:db5a533111a3cfd481e7e4fb2bf8bef69f4fa100339803e0504dd5aecafb96a5",
+                "sha256:dca9dc58845a9d083f302732a3130c68ded845ad5d463865d464e53c75a3dd45",
+                "sha256:e9773e07684a55f54657df05237267611a77b294ec3bacb5f851c4ffca38a465",
+                "sha256:eb60be66accc9a9ea1018b591a1f5800cba83491d07e9acc8c56bc6e6607ab54",
+                "sha256:f60c04c5561b1ddf85ca4e626943cc09d7fb684e1adb22abb632095415a40fd7",
+                "sha256:fdff88393c25016f417770d82678423fc7a56995abb2df3d2a1e55725db6977d"
+            ],
+            "version": "==4.0.0"
+        },
+        "pycparser": {
+            "hashes": [
+                "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
+                "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==2.20"
+        },
+        "pythonsed": {
+            "git": "https://github.com/GillesArcas/PythonSed",
+            "ref": "a746cbd15dd2aed738ce97abae38181b268a4b4a"
+        },
+        "requests": {
+            "hashes": [
+                "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
+                "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
+            ],
+            "index": "pypi",
+            "version": "==2.25.1"
+        },
+        "six": {
+            "hashes": [
+                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
+                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+            "version": "==1.16.0"
+        },
+        "slixmpp": {
+            "hashes": [
+                "sha256:9af836dc5747259788651726f062d69a6f430d306ddbd8e6b141c1fec99549e7"
+            ],
+            "index": "pypi",
+            "version": "==1.7.1"
+        },
+        "soupsieve": {
+            "hashes": [
+                "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc",
+                "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"
+            ],
+            "markers": "python_version >= '3.0'",
+            "version": "==2.2.1"
+        },
+        "text-unidecode": {
+            "hashes": [
+                "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
+                "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
+            ],
+            "version": "==1.3"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
+                "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
+                "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
+            ],
+            "version": "==3.10.0.0"
+        },
+        "urllib3": {
+            "hashes": [
+                "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
+                "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
+            ],
+            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+            "version": "==1.26.4"
+        },
+        "yarl": {
+            "hashes": [
+                "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e",
+                "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434",
+                "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366",
+                "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3",
+                "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec",
+                "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959",
+                "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e",
+                "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c",
+                "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6",
+                "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a",
+                "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6",
+                "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424",
+                "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e",
+                "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f",
+                "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50",
+                "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2",
+                "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc",
+                "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4",
+                "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970",
+                "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10",
+                "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0",
+                "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406",
+                "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896",
+                "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643",
+                "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721",
+                "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478",
+                "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724",
+                "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e",
+                "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8",
+                "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96",
+                "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25",
+                "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76",
+                "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2",
+                "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2",
+                "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c",
+                "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a",
+                "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==1.6.3"
+        },
+        "youtube-dl": {
+            "hashes": [
+                "sha256:4e569cb0477428fd96ee6f7e7a6640b7c9416be626ed708ac4b8ada6c5a6ffbe",
+                "sha256:deb489a17e541ec7ac35581375ae94161eb22a7ec3373b1216181a4360c187ab"
+            ],
+            "index": "pypi",
+            "version": "==2021.5.16"
+        }
+    },
+    "develop": {}
+}
diff --git a/README.org b/README.org
new file mode 100644 (file)
index 0000000..121d0e0
--- /dev/null
@@ -0,0 +1,17 @@
+#+TITLE: Angel XMPP bot
+#+AUTHOR: czar
+
+XMPP bot to preview links and file contents
+
+* Configuration
+  copy the =config.ini.default= to =.config.ini= and set your variables accordly
+
+* Running
+  install the dependencies from =requirements.txt=
+  #+BEGIN_QUOTE
+  $ pip install -r requirements.txt
+  #+END_QUOTE
+  and run it with ==python3.8+==
+  #+BEGIN_QUOTE
+  $ python main.py
+  #+END_QUOTE
diff --git a/angel.png b/angel.png
new file mode 100644 (file)
index 0000000..aea2895
Binary files /dev/null and b/angel.png differ
diff --git a/config.ini.default b/config.ini.default
new file mode 100644 (file)
index 0000000..3a7ee43
--- /dev/null
@@ -0,0 +1,5 @@
+[angel]
+jid = angel@example.com
+owner = admin@example.com
+password = b0TPA55W0rD
+autojoin = room1@muc.example.com room2@muc.example.com room3@muc.example.com
\ No newline at end of file
diff --git a/main.py b/main.py
new file mode 100644 (file)
index 0000000..1ed11cb
--- /dev/null
+++ b/main.py
@@ -0,0 +1,370 @@
+import requests
+import bs4
+import youtube_dl
+import random
+import configparser
+import re
+import io
+import mimetypes
+
+from collections import defaultdict
+from PythonSed import Sed
+from slixmpp import ClientXMPP
+from urllib.parse import urlparse, parse_qs, urlunparse
+from pantomime import normalize_mimetype
+
+sed = Sed()
+
+sed_parse = re.compile("(?<!\\\\)[/#]")
+sed_cmd = re.compile("^s[/#].*[/#].*[/#]")
+
+parser = "html.parser"
+user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:10.0)"
+" Gecko/20100101 Firefox/10.0"
+accept_lang = "en-US"
+data_limit = 100000000  # 100MB
+
+headers = {
+    "user-agent": user_agent,
+    "Accept-Language": accept_lang,
+    "Cache-Control": "no-cache",
+}
+
+youtube_links = ["www.youtube.com", "m.youtube.com"]
+
+youtube_link = "youtu.be"
+
+ydl = youtube_dl.YoutubeDL()
+
+invidious_instances = ["invidious.snopyta.org"]
+
+block_list = ("localhost", "127.0.0.1", "0.0.0.0")
+
+req_list = ("http://", "https://")
+
+html_files = ("text/html", "application/xhtml+xml")
+
+
+class Lifo(list):
+    """
+    Limited size LIFO array to store messages and urls
+    """
+
+    def __init__(self, size):
+        super().__init__()
+        self.size = size
+
+    def add(self, item):
+        self.insert(0, item)
+        if len(self) > self.size:
+            self.pop()
+
+
+def get_youtube_title(url):
+    try:
+        info = ydl.extract_info(url, download=False)
+        return info["title"]
+    except Exception:
+        return ""
+
+
+def get_invidious_link(yurl):
+    video = yurl.split("/")[-1]
+    instance = random.choice(invidious_instances)
+    return f"https://{instance}/watch?v={video}"
+
+
+def get_yurl(path):
+    yurl = f"https://youtu.be/{path}"
+    return yurl
+
+
+class AngelBot(ClientXMPP):
+    commands = {}
+    muc_commands = {}
+
+    messages = defaultdict(
+        lambda: {
+            "messages": Lifo(100),
+            "links": Lifo(10),
+            "previews": Lifo(10),
+        }
+    )
+
+    def get_urls(self, msg):
+        str_list = msg["body"].strip().split()
+        urls = [u for u in str_list if any(r in u for r in req_list)]
+        return urls
+
+    def send_youtube_info(self, uri, sender, mtype):
+        if uri.netloc == youtube_link:
+            yurl = get_yurl(uri.path)
+        elif "v" in (query := parse_qs(uri.query)):
+            if v := query["v"]:
+                yurl = get_yurl(v[0])
+            else:
+                return
+        if output := get_youtube_title(yurl):
+            if output in self.messages[sender]["previews"]:
+                return
+            self.messages[sender]["previews"].add(output)
+
+            invidious = get_invidious_link(yurl)
+            self.send_message(mto=sender, mbody=f"*{output}*", mtype=mtype)
+            self.send_message(mto=sender, mbody=invidious, mtype=mtype)
+
+    async def parse_uri(self, uri, sender, mtype):
+        netloc = uri.netloc
+        if netloc in (youtube_links + [youtube_link]):
+            self.send_youtube_info(uri, sender, mtype)
+        elif netloc.split(":")[0] in block_list:
+            return
+        else:
+            await self.process_link(uri, sender, mtype)
+
+    async def process_link(self, uri, sender, mtype):
+        url = urlunparse(uri)
+        try:
+            r = requests.get(url, stream=True, headers=headers, timeout=5)
+            if r.status_code != 200:
+                return
+        except Exception:
+            return
+
+        ftype = normalize_mimetype(r.headers.get("content-type"))
+
+        if ftype in html_files:
+            data = ""
+            for i in r.iter_content(chunk_size=1024, decode_unicode=False):
+                data += i.decode("utf-8", errors="ignore")
+                if len(data) > data_limit or "</head>" in data.lower():
+                    break
+            soup = bs4.BeautifulSoup(data, parser)
+            if title := soup.find("title"):
+                output = title.text.strip()
+                if output:
+                    output = f"*{output}*" if ("\n" not in output) else output
+                    self.send_message(mto=sender, mbody=output, mtype=mtype)
+        else:
+            try:
+                lenght = int(r.headers.get("content-length") or "0")
+                if not lenght or (lenght > data_limit):
+                    return
+                outfile = io.BytesIO()
+                for chunk in r.iter_content(
+                    chunk_size=512,
+                    decode_unicode=False,
+                ):
+                    outfile.write(chunk)
+
+                await self.embed_file(url, sender, mtype, ftype, outfile)
+            except Exception as e:
+                print(e)
+
+    async def embed_file(self, url, sender, mtype, ftype, outfile):
+        ext = mimetypes.guess_extension(ftype)
+        filename = f"file{ext}"
+        furl = await self.plugin["xep_0363"].upload_file(
+            filename, content_type=ftype, input_file=outfile
+        )
+        message = self.make_message(sender)
+        message["body"] = furl
+        message["type"] = mtype
+        message["oob"]["url"] = furl
+        message.send()
+
+    async def parse_urls(self, msg, urls, sender, mtype):
+        for u in urls:
+            if u in self.messages[sender]["links"]:
+                continue
+            else:
+                self.messages[sender]["links"].add(u)
+
+            uri = urlparse(u)
+            await self.parse_uri(uri, sender, mtype)
+
+    def muc_word(self, func):
+        name = func.__name__
+        self.muc_commands[name] = func
+        return func
+
+    def muc_command(self, func):
+        name = self.prefix + func.__name__
+        self.muc_commands[name] = func
+        return func
+
+    def word(self, func):
+        name = func.__name__
+        self.commands[name] = func
+        return func
+
+    def command(self, func):
+        name = self.prefix + func.__name__
+        self.commands[name] = func
+        return func
+
+    def s(self, msg, sender, mtype):
+        try:
+            text = msg["body"]
+            if not sed_cmd.match(text):
+                self.messages[sender]["messages"].add(text)
+                return
+            sed_args = sed_parse.split(text)
+
+            if len(sed_args) < 4:
+                return
+
+            for message in self.messages[sender]["messages"]:
+                if sed_args[1] in message:
+                    sed.load_string(text)
+                    fobj = io.StringIO(message)
+                    res = sed.apply(fobj, None)
+                    res = "\n".join(res)
+                    self.messages[sender]["messages"].add(res)
+                    return self.send_message(
+                        mto=sender,
+                        mbody=res,
+                        mtype=mtype,
+                    )
+
+        except Exception as e:
+            print(e)
+            return
+
+    def __init__(
+        self,
+        jid,
+        password,
+        owner,
+        nick="angel",
+        prefix="!",
+        autojoin=None,
+    ):
+        ClientXMPP.__init__(self, jid, password)
+        self.owner = owner
+        self.jid = jid
+        self.prefix = prefix
+        self.nick = nick
+        self.autojoin = autojoin or []
+        self.add_event_handler("session_start", self.session_start)
+        self.add_event_handler("message", self.message)
+        self.add_event_handler("groupchat_message", self.muc_message)
+        self.add_event_handler("disconnected", lambda _: self.connect())
+
+        self.register_plugin("xep_0030")
+        self.register_plugin("xep_0060")
+        self.register_plugin("xep_0054")
+        self.register_plugin("xep_0045")
+        self.register_plugin("xep_0066")
+        self.register_plugin("xep_0084")
+        self.register_plugin("xep_0153")
+        self.register_plugin("xep_0363")
+
+    async def session_start(self, event):
+        self.send_presence()
+        await self.get_roster()
+        for channel in self.autojoin:
+            self.plugin["xep_0045"].join_muc(channel, self.nick)
+        await self.avatar()
+
+    async def avatar(self):
+        with open("angel.png", "rb") as avatar_file:
+            avatar = avatar_file.read()
+
+        avatar_type = "image/png"
+        avatar_id = self.plugin["xep_0084"].generate_id(avatar)
+        avatar_bytes = len(avatar)
+
+        info = {"id": avatar_id, "type": avatar_type, "bytes": avatar_bytes}
+
+        await self.plugin["xep_0153"].set_avatar(
+            jid=self.jid,
+            avatar=avatar,
+            mtype=avatar_type,
+        )
+        await self.plugin["xep_0084"].publish_avatar(avatar)
+        await self.plugin["xep_0084"].publish_avatar_metadata([info])
+
+    async def message(self, msg):
+        if msg["type"] in ("chat", "normal"):
+            mtype = "chat"
+            sender = msg["from"].bare
+            message = msg["body"]
+
+            ctx = message.strip().split()
+            try:
+                if not msg["oob"]["url"]:
+                    if urls := self.get_urls(msg):
+                        await self.parse_urls(msg, urls, sender, mtype)
+            except Exception:
+                pass
+
+            edit = "urn:xmpp:message-correct:0" in str(msg)
+
+            cm = ctx.pop(0)
+            if cm in self.commands:
+                if not edit:
+                    self.commands[cm](msg, ctx, sender)
+            else:
+                self.s(msg, sender, mtype)
+
+    async def muc_message(self, msg):
+        if msg["type"] in ("groupchat", "normal"):
+            mtype = "groupchat"
+            sender = msg["from"].bare
+            if msg["mucnick"] == self.nick:
+                return
+
+            ctx = msg["body"].strip().split()
+            try:
+                if not msg["oob"]["url"]:
+                    if urls := self.get_urls(msg):
+                        await self.parse_urls(msg, urls, sender, mtype)
+            except Exception:
+                pass
+            edit = "urn:xmpp:message-correct:0" in str(msg)
+            cm = ctx.pop(0)
+            if cm in self.muc_commands:
+                if not edit:
+                    self.muc_commands[cm](msg, ctx, sender)
+            else:
+                self.s(msg, sender, mtype)
+
+        @self.muc_word
+        def ping(msg, ctx, sender):
+            if ctx:
+                return
+            bot.send_message(
+                mto=sender, mbody=f"pong: {msg['mucnick']}", mtype="groupchat"
+            )
+
+            @self.word
+            def ping(msg, ctx, sender):
+                if ctx:
+                    return
+                bot.send_message(
+                    mto=sender,
+                    mbody=f"pong: {msg.get_from().bare}",
+                    mtype="chat",
+                )
+
+            @self.command
+            def join(msg, ctx, sender):
+                if sender == bot.owner:
+                    for channel in ctx:
+                        bot.plugin["xep_0045"].join_muc(channel, bot.nick)
+
+
+if __name__ == "__main__":
+    config = configparser.ConfigParser()
+    config.read("config.ini")
+
+    jid = config["angel"]["jid"]
+    password = config["angel"]["password"]
+    owner = config["angel"]["owner"]
+    autojoin = config["angel"]["autojoin"].split()
+
+    bot = AngelBot(jid, password, owner, autojoin=autojoin)
+
+    bot.connect()
+    bot.process()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..f437636
--- /dev/null
@@ -0,0 +1,7 @@
+requests
+slixmpp
+beautifulsoup4
+git+https://github.com/GillesArcas/PythonSed#egg=PythonSed
+youtube-dl
+pantomime
+aiohttp