From adfbeb815fd7777bed142e5889c5e202672e648b Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Sun, 28 Jul 2024 23:33:15 +0100 Subject: [PATCH] Add AI command n stuff --- Pipfile | 4 + Pipfile.lock | 356 ++++++++++++++++++++++++++++++++++++++++++- app/__main__.py | 2 + app/main.py | 20 ++- app/modules/ai.py | 147 ++++++++++++++++++ app/modules/ping.py | 8 - app/modules/yd_dl.py | 330 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 857 insertions(+), 10 deletions(-) create mode 100644 app/modules/ai.py delete mode 100644 app/modules/ping.py create mode 100644 app/modules/yd_dl.py diff --git a/Pipfile b/Pipfile index 6a1917e..817ca7b 100644 --- a/Pipfile +++ b/Pipfile @@ -7,6 +7,10 @@ name = "pypi" nio-bot = {extras = ["e2ee", "cli"], version = "*"} tortoise-orm = {extras = ["asyncpg"], version = "*"} click = "*" +yt-dlp = "*" +humanize = "*" +httpx = "*" +ollama = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 5ae9ff8..bf7c205 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f79e75073905f59837df64e0ed9ada062e6a475b60c6f886ae67743e08d8cbe3" + "sha256": "b274d0bb8911eb4f318acddf1d1a2b348e59117d87e270dc9d1a2fa21d8eea1a" }, "pipfile-spec": 6, "requires": { @@ -228,6 +228,95 @@ ], "version": "==1.2.2" }, + "brotli": { + "hashes": [ + "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208", + "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48", + "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354", + "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a", + "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128", + "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c", + "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088", + "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9", + "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a", + "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3", + "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438", + "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578", + "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b", + "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b", + "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68", + "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d", + "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd", + "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409", + "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da", + "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50", + "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0", + "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180", + "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d", + "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112", + "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc", + "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265", + "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327", + "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95", + "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd", + "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914", + "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0", + "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a", + "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7", + "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0", + "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451", + "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f", + "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e", + "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248", + "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91", + "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724", + "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966", + "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97", + "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d", + "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf", + "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac", + "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951", + "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74", + "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60", + "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c", + "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1", + "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8", + "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d", + "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc", + "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61", + "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460", + "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751", + "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9", + "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1", + "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474", + "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2", + "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6", + "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9", + "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2", + "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467", + "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619", + "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf", + "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408", + "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579", + "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84", + "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b", + "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59", + "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752", + "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80", + "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0", + "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2", + "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3", + "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64", + "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643", + "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e", + "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985", + "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596", + "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2", + "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064" + ], + "markers": "implementation_name == 'cpython'", + "version": "==1.1.0" + }, "cachetools": { "hashes": [ "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474", @@ -301,6 +390,102 @@ "markers": "python_version >= '3.8'", "version": "==1.16.0" }, + "charset-normalizer": { + "hashes": [ + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.3.2" + }, "click": { "hashes": [ "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", @@ -430,8 +615,19 @@ "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5", "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5" ], + "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==0.27.0" }, + "humanize": { + "hashes": [ + "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978", + "sha256:39e7ccb96923e732b5c2e27aeaa3b10a8dfeeba3eb965ba7b74a3eb0e30040a6" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==4.10.0" + }, "hyperframe": { "hashes": [ "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", @@ -584,6 +780,14 @@ "markers": "python_version >= '3.7'", "version": "==6.0.5" }, + "mutagen": { + "hashes": [ + "sha256:719fadef0a978c31b4cf3c956261b3c58b6948b32023078a2117b1de09f0fc99", + "sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719" + ], + "markers": "python_version >= '3.7'", + "version": "==1.47.0" + }, "nio-bot": { "extras": [ "cli", @@ -596,6 +800,15 @@ "markers": "python_version < '3.13' and python_version >= '3.9'", "version": "==1.1.1" }, + "ollama": { + "hashes": [ + "sha256:6ff493a2945ba76cdd6b7912a1cd79a45cfd9ba9120d14adeb63b2b5a7f353da", + "sha256:cd7010c4e2a37d7f08f36cd35c4592b14f1ec0d1bf3df10342cd47963d81ad7a" + ], + "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==0.3.0" + }, "packaging": { "hashes": [ "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", @@ -763,6 +976,44 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==3.20.0" }, + "pycryptodomex": { + "hashes": [ + "sha256:0daad007b685db36d977f9de73f61f8da2a7104e20aca3effd30752fd56f73e1", + "sha256:108e5f1c1cd70ffce0b68739c75734437c919d2eaec8e85bffc2c8b4d2794305", + "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c", + "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458", + "sha256:25cd61e846aaab76d5791d006497134602a9e451e954833018161befc3b5b9ed", + "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc", + "sha256:59af01efb011b0e8b686ba7758d59cf4a8263f9ad35911bfe3f416cee4f5c08c", + "sha256:5dcac11031a71348faaed1f403a0debd56bf5404232284cf8c761ff918886ebc", + "sha256:62a5ec91388984909bb5398ea49ee61b68ecb579123694bffa172c3b0a107079", + "sha256:645bd4ca6f543685d643dadf6a856cc382b654cc923460e3a10a49c1b3832aeb", + "sha256:653b29b0819605fe0898829c8ad6400a6ccde096146730c2da54eede9b7b8baa", + "sha256:69138068268127cd605e03438312d8f271135a33140e2742b417d027a0539427", + "sha256:6e186342cfcc3aafaad565cbd496060e5a614b441cacc3995ef0091115c1f6c5", + "sha256:76bd15bb65c14900d98835fcd10f59e5e0435077431d3a394b60b15864fddd64", + "sha256:7805830e0c56d88f4d491fa5ac640dfc894c5ec570d1ece6ed1546e9df2e98d6", + "sha256:7a710b79baddd65b806402e14766c721aee8fb83381769c27920f26476276c1e", + "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43", + "sha256:82ee7696ed8eb9a82c7037f32ba9b7c59e51dda6f105b39f043b6ef293989cb3", + "sha256:88afd7a3af7ddddd42c2deda43d53d3dfc016c11327d0915f90ca34ebda91499", + "sha256:8af1a451ff9e123d0d8bd5d5e60f8e3315c3a64f3cdd6bc853e26090e195cdc8", + "sha256:8ee606964553c1a0bc74057dd8782a37d1c2bc0f01b83193b6f8bb14523b877b", + "sha256:91852d4480a4537d169c29a9d104dda44094c78f1f5b67bca76c29a91042b623", + "sha256:9c682436c359b5ada67e882fec34689726a09c461efd75b6ea77b2403d5665b7", + "sha256:bc3ee1b4d97081260d92ae813a83de4d2653206967c4a0a017580f8b9548ddbc", + "sha256:bca649483d5ed251d06daf25957f802e44e6bb6df2e8f218ae71968ff8f8edc4", + "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e", + "sha256:cbe71b6712429650e3883dc81286edb94c328ffcd24849accac0a4dbcc76958a", + "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781", + "sha256:d3584623e68a5064a04748fb6d76117a21a7cb5eaba20608a41c7d0c61721794", + "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea", + "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b", + "sha256:ff5c9a67f8a4fba4aed887216e32cbc48f2a6fb2673bb10a99e43be463e15913" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==3.20.0" + }, "pydantic": { "hashes": [ "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", @@ -931,6 +1182,14 @@ "markers": "python_version >= '3.8'", "version": "==0.35.1" }, + "requests": { + "hashes": [ + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + ], + "markers": "python_version >= '3.8'", + "version": "==2.32.3" + }, "rpds-py": { "hashes": [ "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd", @@ -1083,6 +1342,92 @@ "markers": "python_version >= '3.6' and python_version < '4.0'", "version": "==2.1.0" }, + "urllib3": { + "hashes": [ + "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", + "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168" + ], + "markers": "python_version >= '3.8'", + "version": "==2.2.2" + }, + "websockets": { + "hashes": [ + "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b", + "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6", + "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df", + "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b", + "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205", + "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892", + "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53", + "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2", + "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed", + "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c", + "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd", + "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b", + "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931", + "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30", + "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370", + "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be", + "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec", + "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf", + "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62", + "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b", + "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402", + "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f", + "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123", + "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9", + "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603", + "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45", + "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558", + "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4", + "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438", + "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137", + "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480", + "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447", + "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8", + "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04", + "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c", + "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb", + "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967", + "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b", + "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d", + "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def", + "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c", + "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92", + "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2", + "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113", + "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b", + "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28", + "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7", + "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d", + "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f", + "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468", + "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8", + "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae", + "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611", + "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d", + "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9", + "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca", + "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f", + "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2", + "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077", + "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2", + "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6", + "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374", + "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc", + "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e", + "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53", + "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399", + "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547", + "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3", + "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870", + "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5", + "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8", + "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7" + ], + "markers": "python_version >= '3.8'", + "version": "==12.0" + }, "yarl": { "hashes": [ "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", @@ -1178,6 +1523,15 @@ ], "markers": "python_version >= '3.7'", "version": "==1.9.4" + }, + "yt-dlp": { + "hashes": [ + "sha256:7587aa25e236cf7b14bdb9378bbffff51202d901b04202be0cf62cbb56d3b52c", + "sha256:f44b5f33776b4f718900c670fe6e4698fb6fcd426455cd837cf25a1d6d4d9560" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2024.7.25" } }, "develop": {} diff --git a/app/__main__.py b/app/__main__.py index 8a8ab77..e5c0b30 100644 --- a/app/__main__.py +++ b/app/__main__.py @@ -15,7 +15,9 @@ def cli(): def run(): """Runs the bot""" log.info("Starting bot.") + logging.getLogger("nio.rooms").setLevel(logging.WARNING) from .main import bot + run_async(bot.start(access_token=bot.cfg["bot"]["access_token"])) diff --git a/app/main.py b/app/main.py index 255b447..4392cbc 100644 --- a/app/main.py +++ b/app/main.py @@ -6,6 +6,7 @@ import platform import logging import tortoise from pathlib import Path + log = logging.getLogger(__name__) @@ -17,6 +18,8 @@ with open("config.toml", "rb") as fd: class TortoiseIntegratedBot(niobot.NioBot): + cfg: dict + async def start(self, **kwargs): url = config["database"].get("uri") if not url: @@ -47,7 +50,7 @@ bot = TortoiseIntegratedBot( device_id=config["bot"].get("device_id", platform.node()), store_path=str(store.resolve()), command_prefix="h!", - owner_id=config["bot"].get("owner_id", "@nex:nexy7574.co.uk"), + owner_id=config["bot"].get("owner_id") or "@nex:nexy7574.co.uk", ) bot.cfg = config @@ -55,3 +58,18 @@ bot.cfg = config @bot.on_event("ready") async def on_ready(_): log.info("Bot has logged in.") + + +@bot.on_event("command") +async def on_command(ctx: niobot.Context): + log.info("Command %s invoked by %s.", ctx.command, ctx.message.sender) + + +@bot.on_event("command_error") +async def on_command_error(ctx: niobot.Context, exc: Exception): + log.error("Command %s failed.", ctx.command, exc_info=exc) + + +@bot.on_event("command_complete") +async def on_command_complete(ctx: niobot.Context, _): + log.info("Command %s completed.", ctx.command) diff --git a/app/modules/ai.py b/app/modules/ai.py new file mode 100644 index 0000000..e67177d --- /dev/null +++ b/app/modules/ai.py @@ -0,0 +1,147 @@ +import json +import logging +from pathlib import Path +from contextlib import asynccontextmanager + +import httpx +import typing +from ollama import AsyncClient + +import niobot +import tomllib + +if typing.TYPE_CHECKING: + from ..main import TortoiseIntegratedBot + + +@asynccontextmanager +async def ollama_client(url: str) -> AsyncClient: + client = AsyncClient(url) + async with client._client: + yield client + + +class AIModule(niobot.Module): + bot: "TortoiseIntegratedBot" + + async def find_server(self, gpu_only: bool = True) -> dict[str, str | bool] | None: + for name, cfg in self.bot.cfg["ollama"].items(): + url = cfg["url"] + gpu = cfg["gpu"] + if gpu_only and not gpu: + continue + async with ollama_client(url) as client: + try: + await client.ps() + except (httpx.HTTPError, ConnectionError): + continue + else: + return {"name": name, **cfg} + + @staticmethod + def read_users(): + p = Path("./store/users.json") + if not p.exists(): + return {} + return json.loads(p.read_text()) + + @staticmethod + def write_users(users: dict[str, str]): + p = Path("./store/users.json") + with open(p, "w") as _fd: + json.dump(users, _fd) + + @niobot.command("ping") + async def ping_command(self, ctx: niobot.Context): + """Checks the bot is running.""" + reply = await ctx.respond("Pong!") + server = await self.find_server() + if not server: + await reply.edit("Pong :(\nNo servers available.") + return + await reply.edit(f"Pong!\nSelected server: {server['name']}") + + @niobot.command("whitelist.add") + @niobot.is_owner() + async def whitelist_add(self, ctx: niobot.Context, user_id: str, model: str = "llama3:latest"): + """[Owner] Adds a user to the whitelist.""" + users = self.read_users() + users[user_id] = model + self.write_users(users) + await ctx.respond(f"Added {user_id} to the whitelist.") + + @niobot.command("whitelist.list") + @niobot.is_owner() + async def whitelist_list(self, ctx: niobot.Context): + """[Owner] Lists all users in the whitelist.""" + users = self.read_users() + if not users: + await ctx.respond("No users in the whitelist.") + return + await ctx.respond("\n".join(f"{k}: {v}" for k, v in users.items())) + + @niobot.command("whitelist.remove") + @niobot.is_owner() + async def whitelist_remove(self, ctx: niobot.Context, user_id: str): + """[Owner] Removes a user from the whitelist.""" + users = self.read_users() + if user_id not in users: + await ctx.respond(f"{user_id} not in the whitelist.") + return + del users[user_id] + self.write_users(users) + await ctx.respond(f"Removed {user_id} from the whitelist.") + + @niobot.command("ollama.set-model") + async def set_model(self, ctx: niobot.Context, model: str): + """Sets the model you want to use.""" + users = self.read_users() + if ctx.message.sender not in users: + await ctx.respond("You must be whitelisted first.") + return + users[ctx.message.sender] = model + self.write_users(users) + await ctx.respond(f"Set model to {model}. Don't forget to pull it with `h!ollama.pull`.") + + @niobot.command("ollama.pull") + async def pull_model(self, ctx: niobot.Context): + """Pulls the model you set.""" + users = self.read_users() + if ctx.message.sender not in users: + await ctx.respond("You need to set a model first. See: `h!help ollama.set-model`") + return + model = users[ctx.message.sender] + server = await self.find_server() + if not server: + await ctx.respond("No servers available.") + return + msg = await ctx.respond(f"Pulling {model} on {server['name']!r}...") + async with ollama_client(server["url"]) as client: + await client.pull(model) + await msg.edit(f"Pulled model {model}.") + + @niobot.command("ollama.chat", greedy=True) + async def chat(self, ctx: niobot.Context): + """Chat with the model.""" + try: + message = " ".join(ctx.args) + users = self.read_users() + if ctx.message.sender not in users: + await ctx.respond("You need to set a model first. See: `h!help ollama.set-model`") + return + model = users[ctx.message.sender] + res = await ctx.respond("Finding server...") + server = await self.find_server() + if not server: + await res.edit(content="No servers available.") + return + async with ollama_client(server["url"]) as client: + await res.edit(content=f"Generating response...") + try: + response = await client.chat(model, [{"role": "user", "content": message}]) + except httpx.HTTPError as e: + response = {"message": {"content": f"Error: {e}"}} + await res.edit(content=response["message"]["content"]) + except Exception as e: + logging.exception(e) + await res.edit(content="An error occurred.") diff --git a/app/modules/ping.py b/app/modules/ping.py deleted file mode 100644 index 867a72b..0000000 --- a/app/modules/ping.py +++ /dev/null @@ -1,8 +0,0 @@ -import niobot - - -class PingModule(niobot.Module): - @niobot.command() - async def ping(self, ctx: niobot.Context): - """Checks if the bot is online.""" - return await ctx.respond("\N{white heavy check mark} Pong! Still alive.") diff --git a/app/modules/yd_dl.py b/app/modules/yd_dl.py new file mode 100644 index 0000000..8d6db84 --- /dev/null +++ b/app/modules/yd_dl.py @@ -0,0 +1,330 @@ +import datetime +import functools +import math +import textwrap +import time +import typing +import uuid +from typing import Optional +from urllib.parse import urlparse + +import httpx +import humanize +import niobot +import logging +import asyncio +import shutil +import subprocess +import tempfile +from pathlib import Path +from yt_dlp import YoutubeDL, DownloadError + + +def utcnow(): + return datetime.datetime.now(tz=datetime.timezone.utc) + + +class YoutubeDLModule(niobot.Module): + def __init__(self, client: niobot.NioBot): + super().__init__(client) + self.log = logging.getLogger("jimmy.cogs.ytdl") + self.common_formats = { + "144p": "bv[width<=144]+ba[ext=webm]/bv[width<=144]+ba[ext=m4a]/bv[width<=144]+ba/b[width<=144]", + "240p": "bv[width<=240]+ba[ext=webm]/bv[width<=240]+ba[ext=m4a]/bv[width<=240]+ba/b[width<=240]", + "360p": "bv[width<=360]+ba[ext=webm]/bv[width<=360]+ba[ext=m4a]/bv[width<=360]+ba/b[width<=360]", + "480p": "bv[width<=500]+ba[ext=webm]/bv[width<=500]+ba[ext=m4a]/bv[width<=500]+bab[width<=480]", + "720p": "bv[width<=720]+ba[ext=webm]/bv[width<=720]+ba[ext=m4a]/bv[width<=720]+ba/b[width<=720]", + "1080p": "bv[width<=1080]+ba[ext=webm]/bv[width<=1080]+ba[ext=m4a]/bv[width<=1080]+ba", + "1440p": "bv[width<=1440]+ba[ext=webm]/bv[width<=1440]+ba[ext=m4a]/bv[width<=1440]+ba", + "2160p": "bv[width<=2160]+ba[ext=webm]/bv[width<=2160]+ba[ext=m4a]/bv[width<=2160]+ba", + "mp3": "ba[filesize<1000M]", + "m4a": "ba[ext=m4a][filesize<1000M]", + "opus": "ba[ext=webm][filesize<1000M]", + "vorbis": "ba[ext=webm][filesize<1000M]", + "ogg": "ba[ext=webm][filesize<1000M]", + } + self.default_options = { + "noplaylist": True, + "nocheckcertificate": True, + "no_color": True, + "noprogress": True, + "logger": self.log, + "format": "((bv+ba/b)[vcodec!=h265][filesize<1000M]/b[filesize<=1000M]/b)", + "outtmpl": "%(title).50s.%(ext)s", + "format_sort": [ + "vcodec:h264", + "acodec:aac", + "vcodec:vp9", + "acodec:opus", + "acodec:vorbis", + "vcodec:vp8", + "ext", + ], + "merge_output_format": "webm/mp4/mov/m4a/oga/ogg/mp3/mka/mkv", + "source_address": "0.0.0.0", + "concurrent_fragment_downloads": 4, + } + + async def convert_to_m4a(self, file: Path) -> Path: + """ + Converts a file to m4a format. + :param file: The file to convert + :return: The converted file + """ + + def inner(): + if not shutil.which("ffmpeg"): + raise RuntimeError("ffmpeg is not installed.") + new_file = file.with_suffix(".m4a") + args = [ + "-vn", + "-sn", + "-i", + str(file), + "-c:a", + "aac", + "-b:a", + "96k", + "-movflags", + "faststart", + "-y", + str(new_file), + ] + self.log.debug("Running command: ffmpeg %s", " ".join(args)) + process = subprocess.run(["ffmpeg", *args], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if process.returncode != 0: + raise RuntimeError(process.stderr.decode()) + return new_file + + return await asyncio.to_thread(inner) + + @staticmethod + async def upload_to_0x0(name: str, data: typing.IO[bytes], mime_type: str | None = None) -> str: + if not mime_type: + import magic + + mime_type = await asyncio.to_thread(magic.from_buffer, data.read(4096), mime=True) + data.seek(0) + async with httpx.AsyncClient() as client: + response = await client.post( + "https://0x0.st", + files={"file": (name, data, mime_type)}, + data={"expires": 12}, + headers={ + "User-Agent": "CollegeBot (see: https://gist.i-am.nexus/nex/f63fcb9eb389401caf66d1dfc3c7570c)" + }, + ) + if response.status_code == 200: + return urlparse(response.text).path[1:] + response.raise_for_status() + + @niobot.command() + async def ytdl( + self, ctx: niobot.Context, url: str, snip: Optional[str] = None, download_format: Optional[str] = None + ): + """Downloads a video from YouTube or other source""" + response = await ctx.respond("Preparing...") + options = self.default_options.copy() + + description = "" + + with tempfile.TemporaryDirectory(prefix="jimmy-ytdl-") as temp_dir: + temp_dir = Path(temp_dir) + paths = { + target: str(temp_dir) + for target in ( + "home", + "temp", + ) + } + + chosen_format = self.default_options["format"] + if download_format: + if download_format in self.common_formats: + chosen_format = self.common_formats[download_format] + else: + chosen_format = download_format + + options.setdefault("postprocessors", []) + options["format"] = chosen_format + options["paths"] = paths + + with YoutubeDL(options) as downloader: + await response.edit(content="Fetching metadata (step 1/10)") + try: + # noinspection PyTypeChecker + extracted_info = await asyncio.to_thread(downloader.extract_info, url, download=False) + except DownloadError as e: + extracted_info = { + "title": "error", + "thumbnail_url": None, + "webpage_url": url, + "format": "error", + "format_id": "-1", + "ext": "wav", + "format_note": str(e), + "resolution": "1x1", + "fps": "1", + "vcodec": "error", + "acodec": "error", + "filesize": 0, + } + title = "error" + description = str(e) + likes = views = 0 + else: + title = extracted_info.get("title", url) or url + title = textwrap.shorten(title, 100) + webpage_url = extracted_info.get("webpage_url", url) + + chosen_format = extracted_info.get("format") or chosen_format or str(uuid.uuid4()) + chosen_format_id = extracted_info.get("format_id") or str(uuid.uuid4()) + final_extension = extracted_info.get("ext") or "mp4" + format_note = extracted_info.get("format_note", "%s (%s)" % (chosen_format, chosen_format_id)) or "" + resolution = extracted_info.get("resolution") or "1x1" + fps = extracted_info.get("fps", 0.0) or 0.0 + vcodec = extracted_info.get("vcodec") or "h264" + acodec = extracted_info.get("acodec") or "aac" + filesize = extracted_info.get("filesize", extracted_info.get("filesize_approx", 1)) + likes = extracted_info.get("like_count", extracted_info.get("average_rating", 0)) + views = extracted_info.get("view_count", 0) + + lines = [] + if chosen_format and chosen_format_id: + lines.append( + "* Chosen format: `%s` (`%s`)" % (chosen_format, chosen_format_id), + ) + if format_note: + lines.append("* Format note: %r" % format_note) + if final_extension: + lines.append("* File extension: " + final_extension) + if resolution: + _s = resolution + if fps: + _s += " @ %s FPS" % fps + lines.append("* Resolution: " + _s) + if vcodec or acodec: + lines.append("%s+%s" % (vcodec or "N/A", acodec or "N/A")) + if filesize: + lines.append("* Filesize: %s" % humanize.naturalsize(filesize)) + + if lines: + description += "\n" + description += "\n".join(lines) + + await response.edit( + f"# {title}\n\n{description}\n\nProgress: `0% [..........]`\n\nDownloading (step 2/10)" + ) + + last_edit = time.time() + + try: + await asyncio.to_thread(functools.partial(downloader.download, [url])) + except DownloadError as e: + logging.error(e, exc_info=True) + return await response.edit( + f"# Error!\n\nDownload failed:\n```\n{e}\n```", + ) + try: + file: Path = next(temp_dir.glob("*." + extracted_info.get("ext", "*"))) + except StopIteration: + ext = extracted_info.get("ext", "*") + self.log.warning( + "Failed to locate downloaded file. Was supposed to be looking for a file extension of " + "%r amongst files %r, however none were found.", + ext, + list(map(str, temp_dir.iterdir())), + ) + return await response.edit( + f"# Error\n\nFailed to locate downloaded file. Expected a file with the extension {ext}.\n\n" + f"Files: {', '.join(list(map(str, temp_dir.iterdir())))}", + ) + + if snip: + try: + trim_start, trim_end = snip.split("-") + except ValueError: + trim_start, trim_end = snip, None + trim_start = trim_start or "00:00:00" + trim_end = trim_end or extracted_info.get("duration_string", "00:30:00") + new_file = temp_dir / ("output" + file.suffix) + args = [ + "-hwaccel", + "auto", + "-i", + str(file), + "-ss", + trim_start, + "-to", + trim_end, + "-preset", + "fast", + "-crf", + "24", + "-deadline", + "realtime", + "-cpu-used", + "5", + "-movflags", + "faststart", + "-b:a", + "96k", + "-y", + "-strict", + "2", + str(new_file), + ] + await response.edit( + f"# Trimming from {trim_start} to {trim_end}\n\nPlease wait, this may take a couple of minutes." + ) + self.log.debug("Running command: 'ffmpeg %s'", " ".join(args)) + process = await asyncio.create_subprocess_exec( + "ffmpeg", + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + self.log.debug("STDOUT:\n%r", stdout.decode()) + self.log.debug("STDERR:\n%r", stderr.decode()) + if process.returncode != 0: + await response.edit( + f"# Trim failed\n\nError:\n```\n{stderr.decode()}\n```", + ) + file = new_file + + stat = file.stat() + size_bytes = stat.st_size + if size_bytes >= ((500 * 1024 * 1024) - 256): + return await response.edit( + f"# Error\n\nFile is too large to upload. Size: {humanize.naturalsize(size_bytes)}", + ) + + size_megabits = (size_bytes * 8) / 1024 / 1024 + eta_seconds = size_megabits / 20 + await response.edit(content=f"Uploading (ETA: {humanize.naturaldelta(eta_seconds)})...") + views = views or 0 + likes = likes or 0 + try: + if vcodec.lower() in [ + "hevc", + "h265", + "av1", + "av01", + ]: + with file.open("rb") as fb: + part = await self.upload_to_0x0(file.name, fb) + await ctx.respond("https://embeds.video/0x0/" + part) + else: + attachment = await niobot.which(file).from_file(file) + await response.reply(None, attachment) + except ( + ConnectionError, + httpx.HTTPStatusError, + ) as e: + self.log.error(e, exc_info=True) + await response.edit(content=f"# Error\n\nUpload failed:\n```\n{e}\n```") + else: + await response.edit( + content=f"# [Downloaded {title}!]({webpage_url})\n\nViews: {views:,} | Likes: {likes:,}" + )