first commit

This commit is contained in:
*****DEAD ACCOUNT 2020-12-30 02:52:35 -05:00
commit 8090033f6f
No known key found for this signature in database
GPG Key ID: 7AF3499CBA8E6251
24 changed files with 4224 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.#*
conf.yml
BotDB.json
*.pyc

25
Pipfile Normal file
View File

@ -0,0 +1,25 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
irc = "*"
ipython = "*"
pyyaml = "*"
emoji = "*"
beautifulsoup4 = "*"
dnspython = "*"
py-expression-eval = "*"
pyroute2 = "*"
numexpr = "*"
brainfuck-interpreter = "*"
nltk = "*"
tinydb = "*"
feedparser = "*"
requests = "*"
[requires]
python_version = "3.6"

512
Pipfile.lock generated Normal file
View File

@ -0,0 +1,512 @@
{
"_meta": {
"hash": {
"sha256": "ab5714630e56605de2816326ee14b4d91085312571d4992a97d243fb04a0547e"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"backcall": {
"hashes": [
"sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e",
"sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"
],
"version": "==0.2.0"
},
"beautifulsoup4": {
"hashes": [
"sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35",
"sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25",
"sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"
],
"index": "pypi",
"version": "==4.9.3"
},
"brainfuck-interpreter": {
"hashes": [
"sha256:6059fef0f12628ccd8e2fa66e8b8947af56d79373ae7f0a8a2cd89ae05c2f317"
],
"index": "pypi",
"version": "==1.0.0"
},
"certifi": {
"hashes": [
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd",
"sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"
],
"version": "==2020.11.8"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"decorator": {
"hashes": [
"sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760",
"sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"
],
"version": "==4.4.2"
},
"dnspython": {
"hashes": [
"sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7",
"sha256:40bb3c24b9d4ec12500f0124288a65df232a3aa749bb0c39734b782873a2544d"
],
"index": "pypi",
"version": "==2.0.0"
},
"emoji": {
"hashes": [
"sha256:e42da4f8d648f8ef10691bc246f682a1ec6b18373abfd9be10ec0b398823bd11"
],
"index": "pypi",
"version": "==0.6.0"
},
"feedparser": {
"hashes": [
"sha256:1b00a105425f492f3954fd346e5b524ca9cef3a4bbf95b8809470e9857aa1074",
"sha256:f596c4b34fb3e2dc7e6ac3a8191603841e8d5d267210064e94d4238737452ddd"
],
"index": "pypi",
"version": "==6.0.2"
},
"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"
},
"importlib-metadata": {
"hashes": [
"sha256:590690d61efdd716ff82c39ca9a9d4209252adfe288a4b5721181050acbd4175",
"sha256:d9b8a46a0885337627a6430db287176970fff18ad421becec1d64cfc763c2099"
],
"markers": "python_version < '3.8'",
"version": "==3.1.0"
},
"importlib-resources": {
"hashes": [
"sha256:7b51f0106c8ec564b1bef3d9c588bc694ce2b92125bbb6278f4f2f5b54ec3592",
"sha256:a3d34a8464ce1d5d7c92b0ea4e921e696d86f2aa212e684451cb1482c8d84ed5"
],
"markers": "python_version < '3.7'",
"version": "==3.3.0"
},
"ipython": {
"hashes": [
"sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64",
"sha256:9f4fcb31d3b2c533333893b9172264e4821c1ac91839500f31bd43f2c59b3ccf"
],
"index": "pypi",
"version": "==7.16.1"
},
"ipython-genutils": {
"hashes": [
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
],
"version": "==0.2.0"
},
"irc": {
"hashes": [
"sha256:99fd5d1fa1d054dee4fbb81e0d5193dc1e8200db751d5da9a97850a62162b9ab",
"sha256:b5179428448947d364edcdcef8cb34a2a7033b4e78e58fec32d092091eab786f"
],
"index": "pypi",
"version": "==19.0.1"
},
"jaraco.classes": {
"hashes": [
"sha256:116429c2047953f525afdcae165475c4589c7b14870e78b2d068ecb01018827e",
"sha256:c38698ff8ef932eb33d91c0e8fc192ad7c44ecee03f7f585afd4f35aeaef7aab"
],
"markers": "python_version >= '3.6'",
"version": "==3.1.0"
},
"jaraco.collections": {
"hashes": [
"sha256:a7889f28c80c4875bd6256d9924e8526dacfef22cd7b80ff8469b4d312f9f144",
"sha256:be570ef4f2e7290b757449395238fa63d70a9255574624e73c5ff9f1ee554721"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.0"
},
"jaraco.functools": {
"hashes": [
"sha256:9fedc4be3117512ca3e03e1b2ffa7a6a6ffa589bfb7d02bfb324e55d493b94f4",
"sha256:d3dc9f6c1a1d45d7f59682a3bf77aceb685c1a60891606c7e4161e72ecc399ad"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.1"
},
"jaraco.logging": {
"hashes": [
"sha256:31716fe84d3d5df39d95572942513bd4bf8ae0a478f64031eff4c2ea9e83434e",
"sha256:b05ed07101883997a30e05c2472798d86129803d9961a0d1081a3236ad37c52a"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.0"
},
"jaraco.stream": {
"hashes": [
"sha256:287e1cba9f278e0146fdded6bc40518930813a5584579769aeaa1d0bfd178a73",
"sha256:a42357141288bbd55938b9ff464173c078038374ce5ffa1bf31895138acc0f30"
],
"markers": "python_version >= '3.6'",
"version": "==3.0.0"
},
"jaraco.text": {
"hashes": [
"sha256:c87569c9afae14f71b2e1c57f316770ab6981ab675d9c602be1c7981161bacdd",
"sha256:e5078b1126cc0f166c7859aa75103a56c0d0f39ebcafc21695615472e0f810ec"
],
"markers": "python_version >= '2.7'",
"version": "==3.2.0"
},
"jedi": {
"hashes": [
"sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20",
"sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.17.2"
},
"joblib": {
"hashes": [
"sha256:698c311779f347cf6b7e6b8a39bb682277b8ee4aba8cf9507bc0cf4cd4737b72",
"sha256:9e284edd6be6b71883a63c9b7f124738a3c16195513ad940eae7e3438de885d5"
],
"markers": "python_version >= '3.6'",
"version": "==0.17.0"
},
"more-itertools": {
"hashes": [
"sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330",
"sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"
],
"markers": "python_version >= '3.5'",
"version": "==8.6.0"
},
"nltk": {
"hashes": [
"sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"
],
"index": "pypi",
"version": "==3.5"
},
"numexpr": {
"hashes": [
"sha256:0eb8d1a949dcc3eea633438af939f406aaaf240ae69b4ab85ed0c11b8d5e77ee",
"sha256:208597cacb191fe983b4ae05dc9ae8177b17d82a0d9f34719d71ac614744f53b",
"sha256:280c316d56903d20a474c5e03c073371b8879842b8070606cef0c1ea7371933a",
"sha256:2cb778a74f315aafc8eded19781e444269bd45f4ce3095697595e5000dc20f8a",
"sha256:33a610bb775a84ab8ded0af4041df2e931ce7edf5b465ccd9851511429c86d0f",
"sha256:3d83f6f3d6d449eb82a4a5bd56b9d61c9e1ade65b1188052700171051329888b",
"sha256:4655276892b5274015377a4487e1c57cc257c666e5578e12679029cc1124fb08",
"sha256:49f835568c864b444fa6fccf64cc01ff51a6171311742451ac4a176df471f9d8",
"sha256:57b7fcf2d0a1370bc9a380f3a96f6d10e4dfab5081b61a198a8d23b80c33e634",
"sha256:57d9ccd0820b7f5b1bed5100dd54a5ae52c39eb5b7e54317ae29e31ed9bd9edf",
"sha256:583fcf614521edf6eb1326e982d6fe3951dbd451d63e51f7438f0142b491d43f",
"sha256:59984617a50369670a88a0f0b6decdf59a93828dc42e29c8851bcffcedf0695b",
"sha256:659cee220ebe4bf88cb527ca9723d7cb390e93cbae8729ff5e927d06713bad26",
"sha256:687fa9521dbafb130f42d61462f968f211f7eb364f2789c5fbe65d82809ad6b2",
"sha256:78c7040baf20036f0d85308fd5f8322e30d553b8daff1de264394014feb62cc0",
"sha256:7cd5369c2f8cb4bac57571e52bca1a9ccc0260567cefa39ac40680dad0e9df4c",
"sha256:81ff83abc969288673ad37055fef3e5e80cdc87f90245b76c0af9bdef6d5c509",
"sha256:841c23811b00f35b4ce2c330b57c4398ff4a61af4488ce0e013e5039bba68188",
"sha256:84d10e27833a5be6c9a61350cba2acb2f36af1e71c4d47c390b4cc80704ccb55",
"sha256:9e7dbf2a849c34f5e61f9b8119688108f7b5dec97ee8ea2946440bc69a4b28d0",
"sha256:9f91ea6385f743d5ef5ef0a074270a057115d8a4c57625800dd25b5912f563b2",
"sha256:a478e224a23609e1bef45b44a65aad2f158a3072947fc0085c231953b1fafdcd",
"sha256:b0d239d9827e1bcee08344fd05835823bc60aff97232e35a928214d03ff802b1",
"sha256:c169e1424d495b7efefe69c046cbf89ae0dc7a071a89b6b844ae328ac48fccbc",
"sha256:e518918a077478523d89060a8eb59178fd80f7f1273fe1a74088c46163fa49b5",
"sha256:e6a7d0c269a3d9e117072551e78ec5332ece7297f80acf6447d701de0328e7df",
"sha256:eb2bd8656ee2a92b2e928904d6b7ad434f559b1f74a381ff5f36ad987badd1a6"
],
"index": "pypi",
"version": "==2.7.1"
},
"numpy": {
"hashes": [
"sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db",
"sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce",
"sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1",
"sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512",
"sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2",
"sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757",
"sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9",
"sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2",
"sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08",
"sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b",
"sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb",
"sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc",
"sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac",
"sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83",
"sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36",
"sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387",
"sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f",
"sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad",
"sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c",
"sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414",
"sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37",
"sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764",
"sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753",
"sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909",
"sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6",
"sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63",
"sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9",
"sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949",
"sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab",
"sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c",
"sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3",
"sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893",
"sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15",
"sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"
],
"markers": "python_version >= '3.6'",
"version": "==1.19.4"
},
"parso": {
"hashes": [
"sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea",
"sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.7.1"
},
"pexpect": {
"hashes": [
"sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937",
"sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"
],
"markers": "sys_platform != 'win32'",
"version": "==4.8.0"
},
"pickleshare": {
"hashes": [
"sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
"sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
],
"version": "==0.7.5"
},
"prompt-toolkit": {
"hashes": [
"sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c",
"sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63"
],
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.8"
},
"ptyprocess": {
"hashes": [
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
],
"version": "==0.6.0"
},
"py-expression-eval": {
"hashes": [
"sha256:43038326b686df697f9533895184c15d18769e215abbd8bcecaea607483f35b3",
"sha256:fcbd26d015568752e1f41b263c722acbcc06b55ef2b4a8d0af5aabbc6b4ba16f"
],
"index": "pypi",
"version": "==0.3.10"
},
"pygments": {
"hashes": [
"sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0",
"sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"
],
"markers": "python_version >= '3.5'",
"version": "==2.7.2"
},
"pyroute2": {
"hashes": [
"sha256:774c5ecf05fe40f0f601a7ab33c19ca0b24f00bf4a094e58deaa5333b7ca49b5"
],
"index": "pypi",
"version": "==0.5.14"
},
"pytz": {
"hashes": [
"sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268",
"sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd"
],
"version": "==2020.4"
},
"pyyaml": {
"hashes": [
"sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
"sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
"sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e",
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
"sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
"sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
"sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a",
"sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
"sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
],
"index": "pypi",
"version": "==5.3.1"
},
"regex": {
"hashes": [
"sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538",
"sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4",
"sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc",
"sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa",
"sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444",
"sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1",
"sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af",
"sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8",
"sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9",
"sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88",
"sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba",
"sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364",
"sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e",
"sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7",
"sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0",
"sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31",
"sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683",
"sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee",
"sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b",
"sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884",
"sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c",
"sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e",
"sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562",
"sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85",
"sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c",
"sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6",
"sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d",
"sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b",
"sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70",
"sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b",
"sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b",
"sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f",
"sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0",
"sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5",
"sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5",
"sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f",
"sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e",
"sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512",
"sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d",
"sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917",
"sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"
],
"version": "==2020.11.13"
},
"requests": {
"hashes": [
"sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8",
"sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"
],
"index": "pypi",
"version": "==2.25.0"
},
"sgmllib3k": {
"hashes": [
"sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9"
],
"version": "==1.0.0"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"soupsieve": {
"hashes": [
"sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55",
"sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232"
],
"markers": "python_version >= '3.0'",
"version": "==2.0.1"
},
"tempora": {
"hashes": [
"sha256:9af06854fafb26d3d40d3dd6402e8baefaf57f90e48fdc9a94f6b22827a60fb3",
"sha256:e319840007c2913bf2f14ebf1f71b94812335d1ef4ca178e1c65c445a2d63da8"
],
"markers": "python_version >= '3.6'",
"version": "==4.0.1"
},
"tinydb": {
"hashes": [
"sha256:1d102d06f9bb22d739d8061b490c64d420de70dca5f95ebd43a492c43c7bd303",
"sha256:c8a8887269927e077f3aa16fddbf4debd176c10edc4ac8a5ce48ced0b10adf8c"
],
"index": "pypi",
"version": "==4.3.0"
},
"tqdm": {
"hashes": [
"sha256:5c0d04e06ccc0da1bd3fa5ae4550effcce42fcad947b4a6cafa77bdc9b09ff22",
"sha256:9e7b8ab0ecbdbf0595adadd5f0ebbb9e69010e0bd48bbb0c15e550bf2a5292df"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.54.0"
},
"traitlets": {
"hashes": [
"sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44",
"sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"
],
"version": "==4.3.3"
},
"urllib3": {
"hashes": [
"sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08",
"sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"
],
"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.2"
},
"wcwidth": {
"hashes": [
"sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
"sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
],
"version": "==0.2.5"
},
"zipp": {
"hashes": [
"sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
"sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
],
"markers": "python_version < '3.8'",
"version": "==3.4.0"
}
},
"develop": {}
}

111
README.md Normal file
View File

@ -0,0 +1,111 @@
# Quickstart
- This bot uses notices to send information that is personalized or private to you, use one of the following two
sections (irssi or weechat)
## Irssi
- `/WINDOW LEVEL +NOTICES`
## Weechat
- `/set irc.msgbuffer.notice = current`
## Bot
- `pipenv` is required, install it with `pip install pipenv` or your OS package manager.
- Clone this repository
- copy the configuration example, to `config.yml` and edit it. Not much is required to start, just the obvious settiings.
- run `pipenv shell`
- run `pipenv sync`
- run `python -m mcmxi.mcmxi`
### Console
- the bot uses IPython embedded for control and development
```
python -m mcmxi.mcmxi
INFO|mcmxi-><module> bot is initialized, press C-d at any point for a smooth and easy exit, starting REPL...
Python 3.6.10 (default, Jan 16 2020, 09:12:04) [GCC]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.
...
```
### Extensibility
- To extend the bot, create a script to the following effect:
```
from mcmxi import mcmxi
if __name__ == "__main__":
l = UTIL.setup_logging(bot_config())
bots = mcmxi.start_bots()
l.info("bot is initialized, press C-d at any point for a smooth and easy exit, starting REPL...")
IPYSH()
mcmxi.stop_bots(bots)
S.exit()
```
- then add a tuple containing an instance of a class that contains your fantasy command functions, the prefix of the method
names if any or "", and a lambda method call wrapper to any or all of the bot instance's executables list. Each bot has an
executable list that looks like this:
```
self.executables = [
(self,
"cmd",
lambda event, instance: instance(event) ),
(CARD,
"cmd",
lambda event, instance: instance(event) ),
(EXP_EVAL,
"cmd",
lambda event, instance: instance(event) ),
(HTTP,
"cmd",
lambda event, instance: instance(event) ),
(IRC_TXT,
"cmd",
lambda event, instance: instance(event) ),
(TERM_BIN,
"cmd",
lambda event, instance: instance(event) ),
(UTIL,
"cmd",
lambda event, instance: instance(event) ) ]
```
- `CARD` is the singleton class
- `cmd` is the prefix for methods that should be exposed as fantasy commands
- `lambda event, instance: instance(event)` is a method call wrapper (default example)
When fantasy commands are sent to a channel, an executor defined in the `UTILS` class is invoked,
which is technically also extensible / customizable. The default simply looks for methods in
each of the whitelisted classes (in `self.executables`) for method's prefixed with `cmd` or
whatever prefix is specified, the rest of the fantasy command that was specified corresponds
directly to the rest of the method name:`
```
self.executor = (
lambda bot, obj, method_prefix, call_wrapper, event: call_wrapper(
event,
obj.__getattribute__(next(filter(lambda t: (
method_prefix != ""
and "{prefix}_{command}"
or "{prefix}{command}").format(
prefix = method_prefix,
command = t[ 0 ]
) == t[ 1 ] and t[ 1 ], zip(ITER.cycle([event.data(
).arguments[ 0 ].split(" "
)[ 0 ].strip(next(filter(lambda x: x.channel_name(
).lower(
) == event.target().lower(
), bot.conf.channels() )
).command_prefix() ) ]
), dir(obj) ) )
)[ 1 ] ) ) )
```

108
conf.example.yml Normal file
View File

@ -0,0 +1,108 @@
bot:
name: 'MMXXI'
dns_servers:
- '4.2.2.1'
- '8.8.4.4'
- '4.2.2.2'
- '8.8.8.8'
log_level: 'info'
networks:
- efnet:
nickname: 'MMXXI'
alt_nickname: 'MMXXI_'
username: 'MMXXI/whatevernet'
password: 'password'
servers:
- 'znc':
retry_connect: true
retry_connect_delay: 2
retry_connect_max_tries: 5
address: '127.0.0.1'
port: 6667
channels:
- '#bot-testing':
command_prefix: "|"
preview_url: true
protect_topic: true
command_delay: 3
modes:
- '+s'
- '+p'
- '+i'
- '+n'
- '#foo':
command_prefix: "|"
preview_url: false
protect_topic: true
command_delay: 3
modes:
- '+s'
- '+p'
- '+i'
- '+n'
- '#bar':
ignore:
- 'spammer!*@*'
- 'anotherbot!*@*'
command_prefix: "%"
preview_url: false
command_delay: 3
modes:
- '+s'
- '+p'
- '+i'
- '+n'
users:
- 'lame':
hostmask: 'lame!lamer@lame.host'
modes:
- '+o'
owners:
- 'lame':
hostmask: 'lame!lamer@lame.host'
users:
- 'default':
hostmask: 'testuser!*@*'
modes:
- '+v'
- 'lame':
hostmask: 'lame!lamer@lame.host'
modes:
- '+o'
- honk:
nickname: 'XXXVII'
username: 'XXXVII/othernetwork'
alt_nickname: 'XXXVII_'
password: 'changeme'
servers:
- 'znc':
address: '127.0.0.1'
port: 7000
channels:
- '&':
ignore:
- 'CDIX!*@*'
command_prefix: "%"
preview_url: true
command_delay: 3
modes:
- '+n'
- '+t'
users:
- 'lame':
hostmask: 'lamer!lame@lame.host'
modes:
- '+o'
owners:
- 'lamer':
hostmask: 'lamer!lame@lame.host'
users:
- 'default':
hostmask: 'testuser!*@*'
modes:
- '+v'
- 'lame':
hostmask: 'lame!lamer@lame.host'
modes:
- '+o'

288
dev.md Normal file
View File

@ -0,0 +1,288 @@
# Notes for development
```
Various JSON APIs
https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=Craig%20Noone&format=json
https://www.coingecko.com/en/api#
https://api.coingecko.com/api/v3/coins/bitcoin?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true
https://searx.everdot.org/?q=hi&categories=general&language=en-US&format=json
https://searx.everdot.org/?q=site%3Atextfiles.com&categories=files&language=en-US&format=json
<form action="https://tinyurl.com/create.php" method="post" target="_blank">
<table align="center" cellpadding="5" bgcolor="#E7E7F7"><tr><td>
<b>Enter a long URL to make <a href="https://tinyurl.com">tiny</a>:</b><br/>
<input type="text" name="url" size="30"><input type="submit" name="submit" value="Make TinyURL!">
</td></tr></table>
</form>
bot API docs and examples
https://github.com/jaraco/irc/blob/master/irc/events.py
https://pypi.org/project/unshare/
https://github.com/docker/docker-py
https://dnspython.readthedocs.io/en/stable/installation.html
https://dnspython.readthedocs.io/en/latest/resolver-class.html
https://github.com/jaraco/irc/blob/71891ea3034067d6678da21bd6d6046ee2d06df5/irc/bot.py#L254
character sets
https://jrgraphix.net/research/unicode_blocks.php
Notes on bs4:
[ meta.attrs['content'] for meta in doc.find_all('meta') if 'name' in meta.attrs and meta.attrs['name'] == 'description' ]
could provide additional info
https://api.coingecko.com/api/v3/coins/bitcoin?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true
notes:
from statistics import mean
set([x['market']['name'] for x in b['tickers']])
mean(sorted([x['converted_last']['usd'] for x in b['tickers']]))
In [13]: [ x.attrs for x in HTML(bots[0][0].grab("https://www.youtube.com/watch?v=rvfEldHBnqM"), features = "html.parser").find("head") if type(x) == SOUP.element.Tag if x.attrs.get("property") !
...: = None and x.attrs.get("property").startswith("og:") ]
DEBUG|mcmxi->grab https://www.youtube.com/watch?v=rvfEldHBnqM
DEBUG|connectionpool->_new_conn Starting new HTTPS connection (1): www.youtube.com:443
DEBUG|connectionpool->_make_request https://www.youtube.com:443 "GET /watch?v=rvfEldHBnqM HTTP/1.1" 200 None
DEBUG|mcmxi->grab <Response [200]>
Out[13]:
[{'property': 'og:site_name', 'content': 'YouTube'},
{'property': 'og:url',
'content': 'https://www.youtube.com/watch?v=rvfEldHBnqM'},
{'property': 'og:title', 'content': 'Still A G Thang'},
{'property': 'og:image',
'content': 'https://i.ytimg.com/vi/rvfEldHBnqM/maxresdefault.jpg'},
{'property': 'og:image:width', 'content': '1280'},
{'property': 'og:image:height', 'content': '720'},
{'property': 'og:description',
'content': 'Provided to YouTube by Universal Music Group Still A G Thang · Snoop Dogg The Best Of Snoop Dogg ℗ 1998 Priority Records, LLC Released on: 2005-01-01 Associa...'},
{'property': 'og:type', 'content': 'video.other'},
{'property': 'og:video:url',
'content': 'https://www.youtube.com/embed/rvfEldHBnqM'},
{'property': 'og:video:secure_url',
'content': 'https://www.youtube.com/embed/rvfEldHBnqM'},
{'property': 'og:video:type', 'content': 'text/html'},
{'property': 'og:video:width', 'content': '960'},
{'property': 'og:video:height', 'content': '720'},
{'property': 'og:video:tag', 'content': 'Snoop Dogg'},
{'property': 'og:video:tag', 'content': 'スヌープ・ドッグ'},
{'property': 'og:video:tag', 'content': 'スヌープドッグ'},
{'property': 'og:video:tag', 'content': 'The Best Of Snoop Dogg'},
{'property': 'og:video:tag', 'content': 'Still A G Thang'},
{'property': 'og:video:tag', 'content': 'スティル・ア・G・サング'},
{'property': 'og:video:tag', 'content': 'スティル・ア・ジー・サング'}]
In [23]: (lambda l: [x.attrs for x in HTML(bots[0][0].grab(l), features = "html.parser").find("head") if type(x) == SOUP.element.Tag if x.attrs.get("property") != None and x.attrs.get("property")
...: .startswith("og:")])("https://www.microsoft.com")
DEBUG|mcmxi->grab https://www.microsoft.com
DEBUG|connectionpool->_new_conn Starting new HTTPS connection (1): www.microsoft.com:443
DEBUG|connectionpool->_make_request https://www.microsoft.com:443 "GET / HTTP/1.1" 302 0
DEBUG|mcmxi->grab https://www.microsoft.com/en-au/
DEBUG|connectionpool->_new_conn Starting new HTTPS connection (1): www.microsoft.com:443
DEBUG|connectionpool->_make_request https://www.microsoft.com:443 "GET /en-au/ HTTP/1.1" 200 39573
DEBUG|mcmxi->grab <Response [200]>
Out[23]:
[{'property': 'og:url', 'content': 'https://www.microsoft.com/en-au'},
{'property': 'og:title', 'content': 'Microsoft - Official Home Page'},
{'property': 'og:description',
'content': 'At Microsoft our mission and values are to help people and businesses throughout the world realize their full potential.'},
{'property': 'og:type', 'content': 'website'}]
In [24]:
In [77]: [ x for x in HTML(bots[0][0].grab("https://imgur.com/a/AjZCl0b"), features="html.parser").find_all("meta") if type(x) == SOUP.element.Tag if x.get("property") != None and x.get("property
...: ").startswith("og:")]
DEBUG|mcmxi->grab https://imgur.com/a/AjZCl0b
DEBUG|connectionpool->_new_conn Starting new HTTPS connection (1): imgur.com:443
DEBUG|connectionpool->_make_request https://imgur.com:443 "GET /a/AjZCl0b HTTP/1.1" 200 None
DEBUG|mcmxi->grab <Response [200]>
Out[77]:
[<meta content="Imgur" property="og:site_name"> <meta content="12331492" property="fb:admins"/> <meta content="12301369" property="fb:admins"/> <meta content="127621437303857" property="fb:app_id"/> <meta content="imgur://imgur.com/?from=fbreferral" property="al:android:url"/> <meta content="Imgur" property="al:android:app_name"/> <meta content="com.imgur.mobile" property="al:android:package"/> <meta content="imgur://imgur.com/?from=fbreferral" property="al:ios:url"/> <meta content="639881495" property="al:ios:app_store_id"/> <meta content="Imgur" property="al:ios:app_name"/> <meta content="https://imgur.com/" property="al:web:url"/> <meta content="@imgur" name="twitter:site"/> <meta content="imgur.com" name="twitter:domain"/> <meta content="com.imgur.mobile" name="twitter:app:id:googleplay"/> <meta content="Imgur" property="author"/> <meta content="Imgur" property="article:author"/> <meta content="https://www.facebook.com/imgur" property="article:publisher"/> <title>Imgur: The magic of the Internet</title><meta content="https://imgur.com/a/AjZCl0b" data-react-helmet="true" property="og:url"/><meta content="https://i.imgur.com/V0jL8lNh.jpg" data-react-helmet="true" name="twitter:image"/><link href="https://api.imgur.com/oembed.json?url=https://imgur.com/a/AjZCl0b" rel="alternate" title="Imgur: The magic of the Internet" type="application/json+oembed"/><link href="https://api.imgur.com/oembed.xml?url=https://imgur.com/a/AjZCl0b" rel="alternate" title="Imgur: The magic of the Internet" type="application/xml+oembed"/><meta href="https://imgur.com/a/AjZCl0b" rel="canonical"/><meta content="600" data-react-helmet="true" property="og:image:width"/><meta content="315" data-react-helmet="true" property="og:image:height"/><meta content="https://i.imgur.com/V0jL8lN.png?fb" data-react-helmet="true" property="og:image"/><meta content="article" data-react-helmet="true" property="og:type"/><meta content="summary_large_image" data-react-helmet="true" name="twitter:card"/><meta content="imgur.com" data-react-helmet="true" name="twitter:title"/><meta content="Imgur: The magic of the Internet" data-react-helmet="true" name="twitter:description"/><meta content="imgur.com" data-react-helmet="true" property="og:title"/><meta content="Imgur: The magic of the Internet" data-react-helmet="true" property="og:description"/> <script>dataLayer=[];var pbjs=pbjs||{};pbjs.que=pbjs.que||[]</script> <script async="true">!function(){var e=document.createElement("script"),t=document.getElementsByTagName("script")[0],n="https://quantcast.mgr.consensu.org".concat("/choice/","f8oruOqDFlMeI","/","imgur.com","/choice.js"),a=0;e.async=!0,e.type="text/javascript",e.src=n,e.onload=function(){var e=document.createEvent("Event");e.initEvent("cmpLoaded",!0,!0),window.dispatchEvent(e)},t.parentNode.insertBefore(e,t),function(){for(var e,i="__tcfapiLocator",a=[],o=window;o;){try{if(o.frames[i]){e=o;break}}catch(e){}if(o===window.top)break;o=o.parent}e||(function e(){var t=o.document,n=!!o.frames[i];if(!n)if(t.body){var a=t.createElement("iframe");a.style.cssText="display:none",a.name=i,t.body.appendChild(a)}else setTimeout(e,5);return!n}(),o.__tcfapi=function(){var e,t=arguments;if(!t.length)return a;if("setGdprApplies"===t[0])3<t.length&&2===t[2]&&"boolean"==typeof t[3]&&(e=t[3],"function"==typeof t[2]&&t[2]("set",!0));else if("ping"===t[0]){var n={gdprApplies:e,cmpLoaded:!1,cmpStatus:"stub"};"function"==typeof t[2]&&t[2](n)}else a.push(t)},o.addEventListener("message",function(a){var i="string"==typeof a.data,e={};try{e=i?JSON.parse(a.data):a.data}catch(e){}var o=e.__tcfapiCall;o&&window.__tcfapi(o.command,o.version,function(e,t){var n={__tcfapiReturn:{returnValue:e,success:t,callId:o.callId}};i&&(n=JSON.stringify(n)),a.source.postMessage(n,"*")},o.parameter)},!1))}();var i=function(){var e=arguments;typeof window.__uspapi!==i&&setTimeout(function(){void 0!==window.__uspapi&&window.__uspapi.apply(window.__uspapi,e)},500)};if(void 0===window.__uspapi){window.__uspapi=i;var o=setInterval(function(){a++,window.__uspapi===i&&a<3?console.warn("USP is not accessible"):clearInterval(o)},6e3)}}()</script> <link href="https://s.imgur.com/desktop-assets/css/styles.ebc99cf807f6b7c8c39c.css" rel="stylesheet"/></meta>,
<meta content="https://imgur.com/a/AjZCl0b" data-react-helmet="true" property="og:url"/>,
<meta content="600" data-react-helmet="true" property="og:image:width"/>,
<meta content="315" data-react-helmet="true" property="og:image:height"/>,
<meta content="https://i.imgur.com/V0jL8lN.png?fb" data-react-helmet="true" property="og:image"/>,
<meta content="article" data-react-helmet="true" property="og:type"/>,
<meta content="imgur.com" data-react-helmet="true" property="og:title"/>,
<meta content="Imgur: The magic of the Internet" data-react-helmet="true" property="og:description"/>]
```
#NLP
- https://medium.com/@ritidass29/create-your-chatbot-using-python-nltk-88809fa621d1
# cards
```
'''
clubs (lowest), followed by diamonds, hearts, and spades (highest).
[(chr(x), x) for x in [9827, 9830, 9829, 9824]]
[(chr(x), x) for x in [9831, 9826, 9825, 9828]]
52 card deck
list(itertools.chain.from_iterable(map(lambda c: [ x for x in [
(c, "A"),
(c, "K"),
(c, "Q"),
(c, "J"),
(c, "10"),
(c, "9"),
(c, "8"),
(c, "7"),
(c, "6"),
(c, "5"),
(c, "4"),
(c, "3"),
(c, "2") ]
], [ chr(x) for x in [0x2660, 0x2661, 0x2666, 0x2667 ] ] ) ) )
Bridge (for bidding and scoring) and occasionally poker: spades, hearts, diamonds, clubs; 'notrump'
ranks above all the suits[clarification needed]
Preferans: hearts, diamonds, clubs, spades. Only used for bidding, and No
Trump[clarification needed] is considered higher than hearts.
Five Hundred: hearts, diamonds, clubs, spades (for bidding and scoring)
Ninety-nine: clubs, hearts, spades, diamonds (supposedly mnemonic as they have
respectively 3, 2, 1, 0 lobes; see article for how this scoring is used)
Skat: clubs, spades, hearts, diamonds; or acorns, leaves, hearts, bells
(for bidding and to determine which Jack beats which in play)
Big Two: spades, hearts, clubs, diamonds (Presidents reverses suit strength:
hearts, diamonds, spades, clubs)
Teen patti: In the case where two players have flushes with cards of the same
rank, the winning hand is based on suit color as ranked by clubs, hearts, spades, diamonds.
Thirteen: hearts, diamonds, clubs, spades.
durak deck:
durak_deck = lambda: [(x, z, ord(x) | rank_value) for x, z, rank_value in itertools.chain.from_iterable(map(lambda c: [ x for x in [ (c,"A", 0x8192), (c, "K", 0x4096), (c, "Q", 0x2048), (c, "J", 0x1024), (c, "10", 0x512), (c, "9", 0x256), (c, "8", 0x128), (c, "7", 0x64), (c, "6", 0x32), (c, "5", 0x16), (c, "4", 0x08), (c, "3", 0x04), (c, "2", 0x02) ] ], [ chr(x) for x in [0x2660, 0x2661, 0x2666, 0x2667 ] ] ) ) if len([y for y in ["A", "K", "Q", "J", "10", "9", "8", "7", "6"] if y == z]) > 0 ]
players = ["deuce", "spigot", "rands"]
current_game_deck = random.sample(durak_deck(), 36)
The game is typically played with two to five people, with six players if desired, using a deck of 36 cards,
for example a standard 52-card deck from which the numerical cards 2 through 5 have been removed.
In theory the limit for a game with one deck of 36 cards is six players, but this extends a
considerable advantage to the player who attacks first, and a considerable disadvantage to the
player who defends first. Variants exist that use more than one deck.
durak_deck = players > 6 and durak_deck * 2 or durak_deck (NOTE to self, color offest inverted?)
The deck is shuffled, and each player is dealt six cards. The bottom card of the stock is
turned and placed face up on the table, its suit determining the trump suit for the current deal.
For example, if it is the 7 of diamonds, then diamonds rank higher than all plain-suit cards.
The rest of the pack is then placed on half over the turnup and at right angles to it,
so that it remains visible. These cards form the prikup or talon. The turnup remains part
of the talon and is drawn as the last card. Cards discarded due to successful defences are
placed in a discard pile next to the talon.
dealer = lambda cards, players: [ cards[y : y + 6 ] for x, y in zip(range(players), [ 0, 6, 12, 18, 24, 30 ] ) ]
hands = dict(zip(players, dealer(current_game_deck, len(players) ) ) )
# note this pack/unpack of the deck in a lambda isn't actually necesarry since the turn up / trump card
# is actually considered to be part of the talon and doesn't need to be "popped" but "peeked",
# but the example remains for other games where a card maybe selectively removed at the same time that
# the stock is assigned:
talon, turn_up = (lambda s: (s, s[ -1 ] ) )(list(itertools.chain.from_iterable(dealer(current_game_deck,
6 )[ len(players) : 6 ])))
self.connection.privmsg(target, "trump card is {1}//{0}".format(turn_up))
The player who has the lowest trump card will be the first attacker (note that there is no obligation to
play that lowest trump card as the first card). The player to the attacker's left is always the defender.
After each round of attack play proceeds clockwise. If the attack succeeds (see below), the defender
loses their turn and the attack passes to the player on the defender's left. If the attack fails,
the defender becomes the next attacker.
In [63]: turn_up Out[64]: ('♠', '9', 9846)
next(enumerate(sorted(itertools.chain.from_iterable([[(x, suite, rank, rank_val
) for suite, rank, rank_val in hands.get(x) if suite == turn_up[0] ] for x in hands.keys()]
), key = lambda k: k[3])))
Out[65]: (0, ('deuce', '♠', '6', 9842))
for reference the hands in this game are:
{ 'deuce': [
('♡', 'A', 42995),
('♦', 'J', 13926),
('♠', 'J', 13924), <-- trump
('♡', '6', 9843),
('♧', 'A', 42999),
('♠', '6', 9842)], <-- trump (lowest)
'spigot': [
('♦', 'A', 42998),
('♡', 'J', 13925),
('♡', 'Q', 9833),
('♡', '7', 9829),
('♦', '10', 10102),
('♧', 'J', 13927)],
'rands': [
('♦', '8', 10094),
('♧', '8', 10095),
('♦', '7', 9830),
('♦', 'K', 26358),
('♧', 'K', 26359),
('♧', '6', 9847)] }
Cards are ranked 6 7 8 9 10 J Q K A. A trump card of any rank beats all cards in the other three suits.
For example, a 6 of trumps beats an ace of any other suit.
The attacker opens their turn by playing a card face up on the table as an attacking card. The player to the
attacker's left is the defender. They respond to the attack with a defending card.
```
# Chess
## cracked out chess board
```
(lambda l: [y for y, _, _ in [(l[x], l[x].append(chr(ord(l[x][1]) + 6)), l[x].append(chr(ord(l[x][0]) + 6))) for x in l]])(dict(list(zip(["a", "b", "c", "d", "e", "f", "g", "h"], [[chr(0x2656), chr(0x2659)] + [None] * 4, [chr(0x2658), chr(0x2659)] + [None] * 4, [chr(0x2657), chr(0x2659)] + [None] * 4, [chr(0x2655), chr(0x2659)] + [None] * 4, [chr(0x2654), chr(0x2659)] + [None] * 4, [chr(0x2657), chr(0x2659)] + [None] * 4, [chr(0x2658), chr(0x2659)] + [None] * 4, [chr(0x2656), chr(0x2659)] + [None] * 4] ) ) ) )
```
# more notes
```
In [12]: test = JSON.loads(bots[0][0].grab("https://en.wikipedia.org/w/api.php?action=query
...: &list=search&srsearch=wtf&format=json") )
I-search backward: test =
In [11]: next(enumerate(test.get("query").get("search")))
Out[11]:
(0,
{'ns': 0,
'title': 'WTF',
'pageid': 423373,
'size': 2534,
'wordcount': 333,
'snippet': '<span class="searchmatch">WTF</span> usually refers to: &quot;What the fuck?&quot;, an expression of disbelief <span class="searchmatch">WTF</span> may also refer to: Work Time Fun, a video game for the PlayStation Portable WTF',
'timestamp': '2020-08-04T23:25:04Z'})
In [12]:
```
## executables notes
```
(M,
"",
lambda target, event, instance: self.connection.privmsg(
target,
self.is_func_or_meth(instance)
and (_ for _ in ()
).throw(Exception("only property references allowed") )
or str(instance) ) ),
(STAT,
"",
lambda target, event, instance: self.connection.privmsg(
target,
self.is_func_or_meth(instance)
and (_ for _ in ()
).throw(Exception("only property references allowed") )
or str(instance) ) ),
```

1
mcmxi/__init__.py Normal file
View File

@ -0,0 +1 @@
#

403
mcmxi/cards.py Normal file
View File

@ -0,0 +1,403 @@
import logging as L
import random as R
import itertools as ITER
from mcmxi.irc_text_formatting import IRC_TXT
'''
'''
class cards():
'''
'''
card_table = {}
def __init__(self):
self.l = L.getLogger(__name__)
# Pretty printer for playing card, every other suite alternates color, order is black, red, black, red
# This will work fine for a 52 card deck, if the unicode values of the enumerated value use the correct
# suit code, eg: (black spade) 0x2660, (red diamond) 0x2661...
self.card_formatter = lambda i, c, index_separator=".": (
"""
{bold} {index} {index_separator}
{_s_} {color} 98,98 {card_edge} {reset}
{color} {color_card_fg},98 {suit} {reset}
{color} 98,98 {separator} {pad} {reset}
{color} {color_card_fg},98 {rank} {reset}
{color} 98,98 {card_edge} {reset}
{_s_}
""").replace(
" ",
""
).replace(
"\n",
""
).format(
bold = IRC_TXT.IRC_FMT_BOLD,
index = i,
index_separator = index_separator,
color = IRC_TXT.IRC_FMT_COLOR,
color_card_fg = ( len( [x for x in [ 0x2660, 0x2663 ] if chr(x) == c[ 0 ] ] ) > 0
and 88
or IRC_TXT.RED[ 3 ][ 0 ] ),
suit = c[ 0 ],
reset = IRC_TXT.IRC_FMT_RESET,
separator = chr(0xFF0F),
card_edge = chr(0x2595),
rank = c[ 1 ],
_s_ = " ",
pad = ( ( c[ 1 ].isdigit()
and int(c[ 1 ]) != 10
or not c[ 1 ].isdigit() )
and " "
or "") )
# 36 card deck suitable for playing durak,
# format is character / rank / weight mask / suit mask
# Suit mask may also be used for weighting.
self.durak_deck = lambda: [ (
x,
z,
ord(x[0]) | rank_value,
rank_value
) for x, z, rank_value, suite_value in ITER.chain.from_iterable(map(
lambda c: [ x for x in [ (
c, "A", 0x16777216, c[ 1 ],
), (c[ 0 ], "K", 0x8388608, c[ 1 ],
), (c[ 0 ], "Q", 0x4194304, c[ 1 ],
), (c[ 0 ], "J", 0x2097152, c[ 1 ],
), (c[ 0 ], "10", 0x1048576, c[ 1 ],
), (c[ 0 ], "9", 0x524288, c[ 1 ],
), (c[ 0 ], "8", 0x262144, c[ 1 ],
), (c[ 0 ], "7", 0x131072, c[ 1 ],
), (c[ 0 ], "6", 0x65536, c[ 1 ],
), (c[ 0 ], "5", 0x32768, c[ 1 ],
), (c[ 0 ], "4", 0x16384, c[ 1 ],
), (c[ 0 ], "3", 0x8192, c[ 1 ],
), (c[ 0 ], "2", 0x4096, c[ 1 ] ) ]
], [ (chr(x), w) for x, w in [
( 0x2660, 0x16 ),
( 0x2665, 0x08 ),
( 0x2666, 0x04 ),
( 0x2663, 0x02 ) ] ] )
) if len([ y for y in [
"A",
"K",
"Q",
"J",
"10",
"9",
"8",
"7",
"6"
] if y == z ] ) > 0 ]
# Durak dealer
self.durak_dealer = lambda cards, players: [ cards[ y : y + 6
] for x, y in zip(range(players
), [
0,
6,
12,
18,
24,
30 ] ) ]
'''
Reset the card table
'''
def cmd_reset_card_table(self, event):
self.card_table[event.target()] = {}
event.bot().connection.privmsg(event.target(), "".join([chr(x) for x in [
0x0028,
0x256F,
0x00B0,
0x25A1,
0x00B0,
0x0029,
0x256F,
0xFE35,
0x0020,
0x253B,
0x2501,
0x253B ] ] ) )
'''
Sets the status of the queued card game to started
'''
def cmd_card_start(self, event):
if self.card_table.get(
event.target()
) and self.card_table.get(
event.target()
).get(
"active"
) and not self.card_table.get(
event.target()
).get("started"):
if len(self.card_table[event.target()]["players"]) >= self.card_table[event.target()]["min_players"]:
event.bot().connection.privmsg(
event.target(),
"{prefix} {bold}{title}{reset} the game has started, dealing...".format(
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
title = self.card_table[event.target()]["title"],
reset = IRC_TXT.IRC_FMT_RESET ) )
self.cmd_card_deal(event)
else:
event.bot().connection.privmsg(
event.target(),
"{prefix} {bold}{title}{reset} there are only {count} players joined, {required} more required to start game".format(
prefix = self.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
title = self.card_table[event.target()]["title"],
reset = IRC_TXT.IRC_FMT_RESET,
count = len(self.card_table[event.target()]["players"]),
required = self.card_table[event.target()]["min_players"]
- len(self.card_table[event.target()]["players"] ) ) )
'''
deals the first or next hand for the queued card game
'''
def cmd_card_deal(self, event):
if self.card_table.get(
event.target()
) and self.card_table.get(
event.target()
).get(
"active"
) and not self.card_table.get(
event.target()
).get("started"):
hands, stock, trump = self.card_table[event.target()]["deal"](
self.card_table[event.target()]["players"],
self.card_table[event.target()]["current_game_deck"] )
self.card_table[event.target()]["hands"] = hands
self.card_table[event.target()]["talon"] = stock
self.card_table[event.target()]["trump"] = trump
event.bot().connection.privmsg(
event.target(),
"{prefix} {bold}{title}{reset} the trump card for this round is {trump_card}".format(
title = self.card_table[event.target()]["title"],
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
trump_card = self.card_formatter(
"",
self.card_table[event.target()]["trump"], "") ) )
[ event.bot().connection.notice(x, "".join( [ self.card_formatter(
y,
z
) for y, z in zip(range(1, len(self.card_table[event.target()]["hands"].get(x) ) + 1
), self.card_table[event.target()]["hands"].get(x) ) ] )
) for x in self.card_table[event.target()]["hands"].keys() ]
'''
takes a turn at the card table
'''
def cmd_card_play(self, event):
cards = self.card_table.get(event.target() ).get("play_cards")(
event.event_arguments(),
self.card_table.get(
event.target()
).get(
"hands"
).get(event.source(
).split("!")[ 0 ] ),
self.card_table.get(event.target() ).get("cards_played") )
if len(cards) > 0:
self.l.debug(cards)
[ (self.card_table.get(
event.target()
).get(
"hands"
).get(event.source_nickname().remove(
x
), self.card_table.get(
event.target()
).get(
"cards_played"
).append( (
event.source_nickname(),
x[ 0 ],
x[ 1 ],
x[ 2 ] ) ) ) ) for x in cards ]
event.bot().connection.privmsg(
event.target(),
"{prefix} {bold}{title}{reset} this round: {played}".format(
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
title = self.card_table.get(
event.target()
).get(
"title"
), played = self.card_table.get(
event.target()
).get(
"end_turn"
)(self.card_table.get(
event.target()
).get(
"cards_played"
), self.card_formatter) ) )
'''
Starts a game of Durak (card game)
'''
def cmd_durak(self, event):
if not self.card_table.get(event.target()).get("active"):
self.cmd_card_game_instructions(event)
self.card_table[event.target()] = {
"active" : True,
"started" : False,
"title" : "durak",
"current_game_deck" : R.sample(self.durak_deck(), 36),
"players" : [],
"min_players" : 1,
"max_players" : 6,
"talon" : None,
"hands" : None,
"trump" : None,
"cards_played" : [],
"end_turn" : lambda cards_played, formatter: "".join( [ formatter(name, (suit, rank), ":")
for name, suit, rank, _, _ in cards_played ] ),
"can_join" : lambda players, min_players, max_players: len(players) < max_players,
"play_cards" : lambda args, hand, played: ( len(played) % 2 == 0
and [ ( hand[ int(args[ 0 ].strip()) - 1 ] ) ] ),
"current_player" : lambda hands, played: next(enumerate(sorted(ITER.chain.from_iterable( [ [ (
x,
suite,
rank,
rank_val
) for suite, rank, rank_val in hands.get(x
) if suite == turn_up[ 0 ] ] for x in hands.keys()] ), key = lambda k: k[ 3 ] ) ) ),
"deal" : lambda players, deck: (dict(zip(
players,
self.durak_dealer(deck, len(players) ) )
), list(ITER.chain.from_iterable(
self.durak_dealer(deck, 6) )
)[ len(players) : 36 ], list(ITER.chain.from_iterable(
self.durak_dealer(deck, 6) ) )[ -1 ] )
}
event.bot().connection.privmsg(
event.target(),
"""
{prefix}
{_s_} {bold} durak {reset}
{_s_} initialized
{_s_} and
{_s_} waiting
{_s_} for
{_s_} players
{_s_} to
{_s_} join.
{_s_} Use
{_s_} the
{_s_} {bold} {cmd_prefix} card_join {reset}
{_s_} command
{_s_} to
{_s_} join
{_s_} the
{_s_} game
{_s_} and
{_s_} then
{_s_} use
{_s_} the
{_s_} {bold} {cmd_prefix} card_start {reset}
{_s_} command
{_s_} to
{_s_} start
{_s_} the
{_s_} game
{_s_} when
{_s_}ready.
""".replace(
" ",
""
).replace(
"\n",
""
).format(
_s_ = " ",
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
cmd_prefix = next(filter(lambda f: f.channel_name(
) == event.target(), event.bot().conf.channels() )
).command_prefix() ) )
self.cmd_card_join(event)
'''
Shows optimal IRC client configuration instructions for playing the card games
'''
def cmd_card_game_instructions(self, event):
event.bot().connection.privmsg(
event.target(),
"""
{prefix}
{_s_} {italic} set
{_s_} your
{_s_} client
{_s_} NOTICE's
{_s_} to
{_s_} current
{_s_} window. {reset}
{_s_} {bold} weechat: {reset}
{_s_} /set
{_s_} irc.msgbuffer.notice
{_s_} =
{_s_} current
{_s_} {bold} irssi: {reset}
{_s_} /WINDOW
{_s_} LEVEL
{_s_} +NOTICES
{_s_} (
{_s_} {italic} more
{_s_} info: {reset}
{_s_} {underline} https://irssi.org/documentation/help/window_properties/ {reset}
{_s_} )
""".replace(
"\n",
""
).replace(
" ",
""
).format(
_s_ = " ",
prefix = IRC_TXT.meander,
italic = IRC_TXT.IRC_FMT_ITALIC,
reset = IRC_TXT.IRC_FMT_RESET,
bold = IRC_TXT.IRC_FMT_BOLD,
underline = IRC_TXT.IRC_FMT_UNDERLINE) )
'''
Joins user to the card table
'''
def cmd_card_join(self, event):
if self.card_table.get(
event.target()
) and self.card_table.get(
event.target()
).get(
"active"
) and not self.card_table.get(
event.target()
).get(
"started"):
self.card_table[event.target()]["players"].append(event.source_nickname())
event.bot().connection.privmsg(event.target(), "{prefix} {bold}durak{reset} {player} joined the game".format(
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
player = event.source_nickname() ) )
CARD = cards()

View File

@ -0,0 +1,39 @@
import numexpr as NE
import brainfuck as BF
from py_expression_eval import Parser as EVAL
'''
'''
class expression_evaluators():
'''
'''
def __self__(self):
self.l = L.getLogger(__name__)
'''
Math expression evaluator
'''
def cmd_eval(self, event) -> None:
event.bot().connection.privmsg(
event.target(),
"{}".format(EVAL(
).parse(" ".join(
event.event_arguments() )
).evaluate(
{} ) ) )
'''
Brainfuck interpreter/evaluator
'''
def cmd_brainfuck(self, event):
raise NotImplementedError()
event.bot().connection.privmsg(
event.target(),
"{}".format(BF.evaluate("".join(event.event_arguments() ) )
).strip(
"\r"
).strip(
"\n") )
EXP_EVAL = expression_evaluators()

245
mcmxi/http_requests.py Normal file
View File

@ -0,0 +1,245 @@
import requests as WWW
import json as JSON
import logging as L
from bs4 import BeautifulSoup as HTML
from mcmxi.irc_text_formatting import IRC_TXT
from mcmxi.utilities import UTIL
'''
'''
class http_requests():
'''
'''
def __init__(self):
self.l = L.getLogger(__name__)
'''
Gets a preview of a link shared by a chatter
'''
def cmd_display_www_page_title(self, event) -> None:
try:
doc = HTML(UTIL.grab(
event.url(),
event,
), features = "html.parser" )
title = None
url = "{}".format(event.url() )
_title = doc.find("title").text
self.l.debug(_title)
if _title != None:
if len(_title.split(" ") ) > 1:
title = "{bold}{title_first_word}{reset} {title_remaining}".format(
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
title_first_word = _title.split(" ")[ 0 ],
title_remaining = " ".join(_title.split(" ")[ 1: ] ) )
else:
title = "{}".format(
_title.split(" ")[ 0 ] )
else:
title = "Untitled"
event.bot().connection.privmsg(
event.target(),
"{prefix} {formatted_title} {underline}{url}{reset}".format(
prefix = IRC_TXT.meander,
formatted_title = title.strip(),
underline = IRC_TXT.IRC_FMT_UNDERLINE,
url = url.strip(),
reset = IRC_TXT.IRC_FMT_RESET ) )
return(doc)
except Exception as ex:
self.l.exception(ex)
'''
Look up info for an IP address
'''
def cmd_ip(self, event) -> None:
res = JSON.loads(WWW.get(
"http://api.bgpview.io/ip/{ip_address}".format(
ip_address = event.event_arguments()[ 0 ]) ).text )
self.l.debug(res)
[ event.bot().connection.privmsg(
event.target(),
"{prefix} {bold}{ptr_record}{reset} AS{as_number} {prefix_sample}".format(
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
ptr_record = res.get("data"
).get("ptr_record"),
as_number = x.get("asn"
).get("asn"),
prefix_sample = " ".join(set([x.get("prefix"
) for x in res.get("data"
).get("prefixes") ] ) ) )
) for x in res.get("data").get("prefixes")[ :1 ] ]
'''
Look up BGP info by ASN
'''
def cmd_asn(self, event) -> None:
res = JSON.loads(WWW.get(
"https://api.bgpview.io/asn/{as_number}/prefixes".format(
as_number = int(event.event_arguments()[ 0 ]) ) ).text )
paste = TERM_BIN.term_bin(JSON.dumps(
res,
sort_keys = True,
indent = True ) )
self.l.debug(paste)
event.bot().connection.privmsg(
event.target(),
" ".join([
"""
{prefix}
{_s_}{bold} {name} {reset}
{_s_} {description}
{_s_} {country}
{_s_} {cidr}
{_s_} {parent}
{_s_}{italic} continued: {reset}
{_s_}{underline} {paste_link} {reset}
""".replace(
" ",
""
).format(
_s_ = " ",
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
italic = IRC_TXT.IRC_FMT_ITALIC,
underline = IRC_TXT.IRC_FMT_UNDERLINE,
reset = IRC_TXT.IRC_FMT_RESET,
name = x.get("name"),
description = x.get("description"),
country = x.get("country_code"),
cidr = x.get("prefix"),
parent = x.get("parent"
).get("rir_name"),
paste_link = paste ) for x in list(res.get("data"
).get("ipv4_prefixes"
) + res.get("data"
).get("ipv6_prefixes")
)[ :1 ] ] ).replace(
"\r\n",
""
).replace(
"\n",
"") )
'''
'''
def cmd_btc(self, event):
raise NotImplementedError()
'''
Gives a free proxy server IP
'''
def cmd_proxy(self, event) -> {}:
res = JSON.loads(WWW.get(
"http://pubproxy.com/api/proxy").text)
self.l.debug(res)
event.bot().connection.privmsg(
event.target(),
"{prefix} {underline}{bold}{protocol_handler}{reset}{underline}://{url}:{port}".format(
prefix = IRC_TXT.meander,
underline = IRC_TXT.IRC_FMT_UNDERLINE,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
protocol_handler = res.get(
"data"
)[ 0 ].get(
"type"
),
url = res.get(
"data"
)[ 0 ].get(
"ip"
),
port = res.get("data"
)[ 0 ].get(
"port"
) ) )
'''
'''
def cmd_wikipedia(self, event) -> None:
raise NotImplementedError()
resp = JSON.loads(UTIL.grab(
"https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch={question}&format=json".format(
question = "".join(event.event_arguments() ) ) ).text )
'''
Searx meta search (YaCY based)
'''
def cmd_searx(self, event) -> None:
req = WWW.get(
"https://searx.everdot.org/?q={}&format=json".format(
" ".join(event.event_arguments() ) ) )
res = JSON.loads(req.text)
self.l.debug(res)
event.bot().connection.privmsg(
event.target(),
"""
{prefix}
{_s_}{bold} {results} {reset} {_s_} results
{_s_}{bold} {answers} {reset} {_s_} answers
{_s_}{bold} {corrections} {reset} {_s_} corrections
{_s_}{bold} {info_boxes} {reset} {_s_} info {_s_} boxes
{_s_}{bold} {suggestions} {reset} {_s_} suggestions
{_s_}{bold} {unresponsive_engines} {reset} {_s_} unresponsive {_s_} engines
{_s_} were {_s_} found {_s_} for {_s_} query
{_s_}{italic} {question} {reset}
{_s_}{underline} {url} {reset}
""".replace(
"\n",
" "
).replace(
" ",
""
).strip(
" "
).format(
_s_ = " ",
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
italic = IRC_TXT.IRC_FMT_ITALIC,
reset = IRC_TXT.IRC_FMT_RESET,
underline = IRC_TXT.IRC_FMT_UNDERLINE,
results = len(res.get(
"results") )
or 0,
answers = len(res.get(
"answers") )
or 0,
corrections = len(res.get(
"corrections") )
or 0,
info_boxes = len(res.get(
"infoboxes") )
or 0,
suggestions = len(res.get(
"suggestions") )
or 0,
unresponsive_engines = len(res.get(
"unresponsive_engines") )
or 0,
question = res.get(
"query"),
url = req.url.replace("&format=json", "") ) )
HTTP = http_requests()

View File

@ -0,0 +1,215 @@
import itertools as ITER
import logging as L
import textwrap as TXT
'''
'''
class irc_text_formatting():
'''
'''
def __init__(self):
self.l = L.getLogger(__name__)
'''
'''
self.default_bg_color = ITER.cycle([ 99 ])
'''
'''
self.art = lambda a: [ (x, chr(y)
) for x, y in zip(range(len(range(0x2500, 0x25FF) )
), range(0x2500, 0x25FF)
) if x == a ][ 0 ][ 1 ]
'''
'''
self.art_reverse = lambda z: [ y for x, y in zip(range(
0x2500,
0x25FE
), range(len(range(
0x2500,
0x25FE) ) )
) if ord(self.art(y)
) == z
or self.art(y
) == z ]
'''
Generic error message
'''
self.fallback = "".join([ chr(x) for x in [
0x00AF,
0x005C,
0x005F,
0x0028,
0x30C4,
0x0029,
0x005F,
0x002F,
0x00AF ] ] )
'''
Default color map for IRC color interpolation
'''
self.default_color_map = (
lambda bg = None: ITER.cycle(ITER.chain.from_iterable([list(map(lambda x: x != 88 and (
x,
(bg != None)
and next(bg)
or next(self.default_color_map)
) or (x - 12, 88), x) ) for x in map(lambda z: [index for index in list(list(ITER.accumulate(range(
z,
z + 7
), lambda x, y: x == 0 and z or x + 12) ),
) ], range(16, 28) ) ] ) ) )
'''
IRC text formatting class property names (bound later)
'''
self.format_chars = [
"IRC_FMT_BOLD",
"IRC_FMT_COLOR",
"IRC_FMT_ITALIC",
"IRC_FMT_REVERSE_COLOR",
"IRC_FMT_UNDERLINE",
"IRC_FMT_STRIKETHROUGH",
"IRC_FMT_MONOSPACE",
"IRC_FMT_RESET" ]
'''
color palette table names, used to store interpolated color palettes as individual lists in class
properties (initialized in constructor)
'''
self.palletes = [
"RED",
"ORANGE",
"YELLOW",
"GREEN",
"GREEN_2",
"GREEN_3",
"AQUA",
"BLUE",
"BLUE_2",
"PURPLE",
"PURPLE_2",
"RED_2" ]
# Color PRIVMSG
self.color_say = (
lambda bot, message, target, pallete = ITER.cycle( [ (0, 1) ]
): [bot.connection.privmsg(target, "".join(["{}{},{}{}{}".format(
self.IRC_FMT_COLOR,
x[ 1 ][ 0 ],
x[ 1 ][ 1 ],
x[ 0 ],
self.IRC_FMT_RESET
) for x in zip(list(index
), pallete ) ] ) ) for index in TXT.wrap(message, 53) ] )
# Random light shades from random color palettes
self.random_light = lambda: ITER.cycle(R.sample([self.__getattribute__(x
)[ 4 ] for x in self.palletes ], len(self.palletes) ) )
# Default message prefix art
self.meander = "{}{}{}".format(
self.art(155),
self.art(156),
self.art(159) )
# Μαίανδρος (3-liner)
self.meandros = [[
self.art(84),
self.art(80),
self.art(80),
self.art(80),
self.art(80),
self.art(87),
" ",
self.art(81)
],[ self.art(81),
" ",
self.art(84),
self.art(80),
self.art(80),
self.art(93),
" ",
self.art(81)
],[ self.art(81),
" ",
self.art(90),
self.art(80),
self.art(80),
self.art(80),
self.art(80),
self.art(93) ] ]
# trans flag
self.trans_flag = lambda: ["".join(map(lambda l: "{color}{fg},{bg}{back_fill}{reset}".format(
color = self.IRC_FMT_COLOR,
fg = x[ 1 ][ 0 ],
bg = x[ 1 ][ 0 ],
back_fill = l,
reset = self.IRC_FMT_RESET ), x[ 0 ])
) for x in zip( [ [ self.art(136) ] * 30 ] * 5, (
self.BLUE[ 5 ],
self.RED_2[ 4 ],
self.RED_2[ 6 ],
self.RED_2[ 4 ],
self.BLUE[ 5 ] ) ) ]
# IRC text formatting escape characters
[ self.__setattr__(y, "{}".format(x) ) for x, y in zip( [
chr(0x02),
chr(0x03),
chr(0x1D),
chr(0x16),
chr(0x1F),
chr(0x1E),
chr(0x11),
chr(0x0F)
], self.format_chars ) ]
# Create color palette class properties from color palette names
[ (not self.__dict__.get(x[ 1 ][ 0 ]
) and (self.__setattr__(x[ 1 ][ 0 ], list()
) == None and self.__getattribute__(x[ 1 ][ 0 ])
) or self.__getattribute__(x[ 1 ][ 0 ] )
).append((x[ 1 ][ 1 ], x[ 1 ][ 2 ] )
) for x in zip(range(7 * 12
), ITER.cycle(ITER.chain.from_iterable([list(map(lambda x: x[ 1 ] != 88 and (x[ 0 ], x[ 1 ], next(self.default_bg_color)
) or (x[ 0 ], x[ 1 ] - 12, next(self.default_bg_color) ), x ) ) for x in map(lambda z: [(z[ 1 ], index
) for index in list(ITER.accumulate(range(z[ 0 ], z[ 0 ] + 7
), lambda x, y: x == 0 and z[ 0 ] or x + 12 )
) ], zip(range(16, 28), self.palletes ) ) ] ) ) ) ]
'''
Simple greeter, runs on !greet command
'''
def cmd_greet(self, target, event) -> None:
self.l.debug(event)
self.color_say(
self,
"ohai2u =)",
target,
ITER.cycle(self.GREEN_2) )
'''
Shrug ASCII
'''
def cmd_shrug(self, target, event) -> None:
self.l.debug(event)
self.color_say(
self,
self.fallback,
target,
self.random_light() )
'''
Gives a random unicode symbol to add to your collection
'''
def cmd_give(self, target, event) -> None:
self.l.debug(event)
self.connection.action(target, "gives you a {unicode_artifact}".format(
unicode_artifact = R.sample(list(EMO.EMOJI_UNICODE.values() ), 1)[ 0 ]) )
IRC_TXT = irc_text_formatting()

295
mcmxi/mcmxi.py Normal file
View File

@ -0,0 +1,295 @@
import threading as T
import queue as Q
import sys as S
import irc.client as C
import logging as L
import time as TIME
import itertools as ITER
from IPython import embed as IPYSH
from dns import resolver as DNS
from mcmxi.objects import *
from mcmxi.cards import CARD
from mcmxi.expression_evaluators import EXP_EVAL
from mcmxi.http_requests import HTTP
from mcmxi.irc_text_formatting import IRC_TXT
from mcmxi.term_bin import TERM_BIN
from mcmxi.utilities import UTIL
'''
'''
class mcmxi(C.SimpleIRCClient):
conf = None
l = None
state = None
'''
Whitelisted executable modules, tuple format is:
module
method prefix or empty string
lambda wrapper function
'''
executables = None
def __str__(self):
return(self.conf.parent().bot_name())
'''
'''
def __init__(self, conf):
self.state = bot_state(self)
self.l = L.getLogger(__name__)
self.conf = conf
UTIL.DNS.nameservers = self.conf.parent().name_servers()
self.executables = [
( self,
"cmd",
lambda event, instance: instance(event) ),
( CARD,
"cmd",
lambda event, instance: instance(event) ),
( EXP_EVAL,
"cmd",
lambda event, instance: instance(event) ),
( HTTP,
"cmd",
lambda event, instance: instance(event) ),
( IRC_TXT,
"cmd",
lambda event, instance: instance(event) ),
( TERM_BIN,
"cmd",
lambda event, instance: instance(event) ),
( UTIL,
"cmd",
lambda event, instance: instance(event) ) ]
self.event_handlers = [
( self.state,
"on",
lambda cls, event, prefix: (
[ getattr(cls, x)(
event) for x in dir(cls)
if x.startswith(prefix)
and "{}_{}".format(
prefix,
event.event_type()
) == x ] ) ),
( bot_event,
"on",
lambda cls, event, prefix: (
[ getattr(cls, x)(
event) for x in dir(cls)
if x.startswith(prefix)
and "{}_{}".format(
prefix,
event.event_type()
) == x ] ) ),
( self,
"on",
lambda cls, event, prefix: (
super()._dispatcher(
cls.connection, event.inner() ) ) ) ]
C.SimpleIRCClient.__init__(self)
'''
appends an event to the event_history list
'''
def add_event_to_event_buffer(self, event):
self.l.info("{}".format(event) )
self.state.event_history.append(event)
return(event)
'''
override for _dispatcher method (jaraco super class)
attempts to find an executable callback handler from
self.event_handlers
'''
def _dispatcher(self, connection, event):
try:
mcmxi_event = bot_event(
event_type = event.type,
bot = self,
data = event.arguments,
target = event.target,
inner = event)
[ (
event.type,
event.arguments,
cls,
prefix,
wrapper(
cls = cls,
event = self.add_event_to_event_buffer(mcmxi_event),
prefix = prefix) ) for cls, prefix, wrapper in self.event_handlers ]
except Exception as ex:
self.l.exception(ex)
'''
bot channel loop, each channel has it's own thread bot_connected this loop and awaits
events from it's allocated command_queue. The call to get on the command queue
blocks until it receives an item.
'''
def process_work_queue(self, channel) -> None:
while self.state.bot_connected:
try:
'''
This is a blocking call, if the queue is empty it waits until
the first event is available before succeeding, good for
this kind of a loop
'''
event = self.state.command_queue[channel.channel_name()].get()
if event.is_fantasy_command():
handled = False
for module_instance, method_prefix, call_wrapper in self.executables:
try:
self.l.debug("{instance} {prefix} {wrapper} ".format(
instance = module_instance,
prefix = method_prefix,
wrapper = call_wrapper ) )
UTIL.executor(
self,
module_instance,
method_prefix,
call_wrapper,
event)
handled = True
except StopIteration:
pass
if not handled:
IRC_TXT.color_say(
self,
IRC_TXT.fallback,
channel.channel_name(),
ITER.cycle(IRC_TXT.GREEN) )
elif event.arguments_contain_urls():
HTTP.cmd_display_www_page_title(event)
except Exception as ex:
if not self.bot_connected:
self.l.debug("self.bot_connected is False, aborting thread")
return
IRC_TXT.color_say(
self,
IRC_TXT.fallback,
channel.channel_name(),
ITER.cycle(IRC_TXT.RED) )
self.l.exception(ex)
finally:
if self.state.bot_connected:
TIME.sleep(channel.command_delay() )
self.state.command_queue[ channel.channel_name() ].task_done()
'''
Main bot loop, do not use super().start,
this is abortable whereas super().start
uses a "process_forever" that doesn't
have an abort condition for it's thread
'''
def start(self):
while not self.state.closing:
if not self.state.bot_connected:
self.state.on_disconnect()
if (self.state.current_retry_count + 1
> self.state.current_server.retry_connect_max_tries() ):
break
continue
self.reactor.process_once()
TIME.sleep(0.2)
'''
Connect factory
'''
def connect(self):
try:
self.state.current_server = self.conf.next_server()
super().connect(
server = self.state.current_server.address(),
port = self.state.current_server.port(),
nickname = self.state.current_server.parent().nickname(),
password = self.state.current_server.parent().password(),
username = self.state.current_server.parent().username() )
self.state.bot_connected = True
except Exception as ex:
self.l.debug("connection failed {} ".format(ex))
self.state.bot_connected = False
finally:
return(self)
'''
Disconnects the IRC connection and shuts down all of the bot threads
'''
def disconnect(self, closing = None):
try:
if self.state.closing != None:
self.state.closing = True
if self.state.bot_connected:
self.state.bot_connected = False
# process_work_queue blocks on a call to queue get, this will unblock it
# and the loop will continue, causing the condition to be re-checked,
# which will cause the loop to break and the thread will abort
[ self.state.command_queue.get(queue).put(None) for queue in self.state.command_queue.keys() ]
self.connection.quit()
except Exception as ex:
self.l.exception(ex)
finally:
return(self)
'''
'''
@staticmethod
def start_bots():
bots = []
bots = [ ( bot, ( lambda t: ( t, t.start() )
)( T.Thread(target = bot.start) )[ 0 ]
) for bot in [ mcmxi(config).connect(
) for config in bot_config().networks() ] ]
return(bots)
'''
'''
@staticmethod
def stop_bots(bots):
[ bot.disconnect(closing = True) for bot, _ in bots ]
if __name__ == "__main__":
l = UTIL.setup_logging(bot_config())
bots = mcmxi.start_bots()
l.info("bot is initialized, press C-d at any point for a smooth and easy exit, starting REPL...")
IPYSH()
mcmxi.stop_bots(bots)
S.exit()

18
mcmxi/objects/__init__.py Normal file
View File

@ -0,0 +1,18 @@
#
from mcmxi.objects.bot_event import bot_event
from mcmxi.objects.bot_config import bot_config
from mcmxi.objects.channel import channel
from mcmxi.objects.network import network
from mcmxi.objects.owner import owner
from mcmxi.objects.server import server
from mcmxi.objects.user import user
from mcmxi.objects.bot_state import bot_state
__all__ = [ "bot_state",
"bot_event",
"bot_config",
"channel",
"network",
"owner",
"server",
"user" ]

View File

@ -0,0 +1,41 @@
import yaml as Y
from mcmxi.objects.network import network
'''
'''
class bot_config():
'''
'''
def __init__(self,
conf = Y.load(
open('conf.yml'),
Loader = Y.FullLoader
).get('bot') ):
self.conf = conf
self._networks = [ network(list(zip(*x.items() )
), self) for x in self.conf.get('networks') ]
'''
'''
def name_servers(self):
return(self.conf.get('dns_servers') )
'''
'''
def bot_name(self):
return(self.conf.get('name') )
'''
'''
def jsonbin_config(self):
return(self.conf.get('jsonbin') )
'''
'''
def networks(self):
return(self._networks)
'''
'''
def log_level(self):
return(self.conf.get('log_level'))

1384
mcmxi/objects/bot_event.py Normal file

File diff suppressed because it is too large Load Diff

101
mcmxi/objects/bot_state.py Normal file
View File

@ -0,0 +1,101 @@
import collections as COL
import logging as L
import time as TIME
from mcmxi.objects.channel import channel
class bot_state():
command_queue = {}
channel_threads = {}
targets = []
event_history = COL.deque(maxlen = 1000)
bot_connected = False
closing = False
current_retry_count = 0
l = L.getLogger(__name__)
'''
returns true if command queue has unfinished tasks or false if not
'''
def command_queue_has_unfinished_tasks(self, event):
return(self.command_queue[ event.target() ].unfinished_tasks > 0)
'''
'''
def __init__(self, bot):
self.bot = bot
'''
channel joined event
'''
def on_join(self, event):
if (event.source_nickname() == event.bot().connection.get_nickname()
and len([x for x in self.targets if x == event.target ]) == 0):
c = [x for x in event.bot().conf.channels() if x.channel_name() == event.target()]
self.targets.append(c != None
and next(enumerate(c))
or channel(None, None, target = event.target() ) )
if self.command_queue.get(event.target()) == None:
self.command_queue[event.target()] = Q.Queue(1)
'''
start channel worker thread
'''
if self.channel_threads.get(event.target()) == None:
chan = T.Thread(target = lambda: event.bot().process_work_queue(event.target()) )
chan.start()
self.channel_threads[event.target()] = chan
'''
Disconnected event
'''
def on_disconnect(self, event = None) -> None:
if self.bot_connected:
self.bot_connected = False
while not self.closing and not self.bot_connected:
if self.bot.connect().state.bot_connected:
self.l.debug("connected")
self.current_retry_count = 0
break
if self.current_retry_count + 1 > self.current_server.retry_connect_max_tries():
self.l.debug("max retry count exceeded, giving up")
break
self.current_retry_count = self.current_retry_count + 1
self.l.debug("reconnect failed try {} of {} will retry in {} seconds".format(
self.current_retry_count,
self.current_server.retry_connect_max_tries(),
self.current_server.retry_connect_delay() ) )
TIME.sleep(self.current_server.retry_connect_delay() )
'''
NAMES list (response) for a channel event
'''
def on_namreply(self, event) -> None:
ch_type, channel, nick_list = event.inner().arguments
if channel == '*':
# User is not in any visible channel
# http://tools.ietf.org/html/rfc2812#section-3.2.5
self.l.debug("user is not in any visible channel")
return
c = next(enumerate([x for x in event.bot().conf.channels() if x.channel_name() == event.target()] ) )
for nick in nick_list.split():
nick_modes = []
if nick[ 0 ] in event.bot().connection.features.prefix:
nick_modes.append(self.connection.features.prefix[nick[ 0 ] ] )
nick = nick[ 1: ]
for mode in nick_modes:
pass

40
mcmxi/objects/channel.py Normal file
View File

@ -0,0 +1,40 @@
class channel():
def __init__(self, chan, parent_config, target=None):
if chan != None:
self._channel_name = chan[0][0]
self.conf = chan[1][0]
self._parent = parent_config
elif target != None:
self._channel_name = target
self.mode_mask = 0
def parent(self):
return(self._parent)
def channel_name(self):
return(self._channel_name)
def modes(self):
return(self.conf.get("modes") )
def command_prefix(self):
return(self.conf.get("command_prefix") )
def url_preview(self):
return(self.conf.get("preview_url") )
def protect_channel_topic(self):
return(self.conf.get("protect_topic") )
def ignore_list(self):
return(self.conf.get("ignore") )
def is_source_ignored(self, source):
return(len([index for index in self.conf.get("ignore") or [] if index == source]) > 0)
def command_delay(self):
return(float(self.conf.get("command_delay") or 0.0 ) )

View File

@ -0,0 +1,8 @@
'''
nick!username@hostname
*!username@hostname
'''
class host_mask():
def __init__(self):
pass

44
mcmxi/objects/network.py Normal file
View File

@ -0,0 +1,44 @@
import itertools as ITER
from mcmxi.objects.server import server
from mcmxi.objects.owner import owner
from mcmxi.objects.user import user
from mcmxi.objects.channel import channel
class network():
def __init__(self, network, parent_config):
self.network_name = network[0][0]
self.conf = network[1][0]
self._servers = ITER.cycle( [server(list(zip(*x.items() ) ), self)
for x in self.conf.get("servers") ] )
self._owners = [owner(list(zip(*x.items() ) ), self)
for x in self.conf.get("owners") ]
self._users = [user(list(zip(*x.items() ) ), self)
for x in self.conf.get("users") ]
self._channels = [channel(list(zip(*x.items() ) ), self)
for x in self.conf.get("channels") ]
self._parent = parent_config
def channels(self):
return(self._channels)
def username(self):
return(self.conf.get("username"))
def nickname(self):
return(self.conf.get("nickname") )
def alt_nickname(self):
return(self.conf.get("alt_nickname") )
def next_server(self):
return(next(self._servers) )
def password(self):
return(self.conf.get("password") )
def owners(self):
return(self._owners)
def parent(self):
return(self._parent)

17
mcmxi/objects/owner.py Normal file
View File

@ -0,0 +1,17 @@
class owner():
def __init__(self, owner, parent_config):
self._owner_name = owner[0][0]
self.conf = owner[1][0]
self._parent = parent_config
def hostmask(self):
return(self.conf.get('hostmask') )
def owner_name(self):
return(self._owner_name)
def owner_password(self):
return(self.conf.get('password') )
def parent(self):
return(self._parent)

23
mcmxi/objects/server.py Normal file
View File

@ -0,0 +1,23 @@
class server():
def __init__(self, server, parent_config):
self.server_name = server[0][0]
self.conf = server[1][0]
self._parent = parent_config
def address(self):
return(self.conf.get('address') )
def port(self):
return(self.conf.get('port') )
def parent(self):
return(self._parent)
def retry_connect(self):
return(self.conf.get('retry_connect') or False)
def retry_connect_delay(self):
return(self.conf.get('retry_connect_delay') or 1)
def retry_connect_max_tries(self):
return(self.conf.get('retry_connect_max_tries') or 1)

17
mcmxi/objects/user.py Normal file
View File

@ -0,0 +1,17 @@
class user():
def __init__(self, user, parent_config):
self._user_name = user[0][0]
self.conf = user[1][0]
self._parent = parent_config
def parent(self):
return(self._parent)
def user_name(self):
return(self._user_name)
def hostmask(self):
return(self.conf.get('hostmask') )
def modes(self):
return(self.conf.get('modes'))

43
mcmxi/term_bin.py Normal file
View File

@ -0,0 +1,43 @@
import socket as SOCK
import logging as L
'''
'''
class term_bin():
'''
'''
def __init__(self):
self.l = L.getLogger(__name__)
'''
Posts data to TermBin and returns a URL to the term-paste
'''
def term_bin(self, data) -> "":
self.l.debug(data)
sock = SOCK.socket(
SOCK.AF_INET,
SOCK.SOCK_STREAM)
server_address = (
'termbin.com',
9999)
sock.connect(server_address)
sock.sendall(bytes(
"{}".format(data),
"UTF-8") )
sock.sendall(bytes(
"\0",
"UTF-8") )
return(sock.recv(
4096
).decode(
).replace(
'\r\n',
'\n') )
TERM_BIN = term_bin()

242
mcmxi/utilities.py Normal file
View File

@ -0,0 +1,242 @@
import itertools as ITER
import sys as S
import logging as L
import urllib.parse as URL
import requests as WWW
from pyroute2.iproute import IPRoute as IP
from ipaddress import IPv4Network as N4, IPv6Network as N6
from mcmxi.irc_text_formatting import IRC_TXT
from dns import resolver as DNS
'''
'''
class utilities():
DNS = DNS.Resolver()
'''
Determine whether type of f is one of the many function or method types
'''
is_func_or_meth = lambda f: (
TYPES.FunctionType
== type(f)
or TYPES.BuiltinFunctionType
== type(f)
or TYPES.BuiltinMethodType
== type(f)
or TYPES.MethodType
== type(f)
or TYPES.LambdaType
== type(f)
) and True or False
'''
'''
def __init__(self):
self.l = L.getLogger(__name__)
# Executes a method call for a given command, for any
# modules whitelisted in self.executables
self.executor = (
lambda bot, obj, method_prefix, call_wrapper, event: call_wrapper(
event,
obj.__getattribute__(next(filter(lambda t: (
method_prefix != ""
and "{prefix}_{command}"
or "{prefix}{command}").format(
prefix = method_prefix,
command = t[ 0 ]
) == t[ 1 ] and t[ 1 ], zip(ITER.cycle([event.data(
).arguments[ 0 ].split(" "
)[ 0 ].strip(next(filter(lambda x: x.channel_name(
).lower(
) == event.target().lower(
), bot.conf.channels() )
).command_prefix() ) ]
), dir(obj) ) )
)[ 1 ] ) ) )
'''
grabs an arbitrary HTTP/s response
'''
def grab(self, url, event = None, depth = 0) -> "":
self.l.debug(url)
req = WWW.get(
url,
stream = True,
allow_redirects = False)
self.l.debug(req)
if req.is_redirect:
if self.verify_address(req.headers.get("Location"
) or req.headers.get("location")
) and depth <= 4:
return(self.grab(req.headers.get("Location"
) or req.headers.get("location"), event, depth + 1) )
else:
raise Exception("no redirect location specified or too many redirects")
ret = []
try:
[ ret.append(index) for index in req.iter_content(
chunk_size = 8192,
decode_unicode = True)
if ( len(bytes("".join(ret), "UTF-8") ) < 500000 * 4 )
or ( _ for _ in () ).throw(StopIteration) ]
except StopIteration:
self.l.debug("max size read")
return("".join(ret) )
'''
Verify that the destination URL doesn't resolve to something
unreasonable, and that it doesn't resolve to any addresses
assigned to the server running the bot
'''
def verify_address(self, url) -> bool:
addr = None
try:
addr = N6("{}/128".format(self.DNS.resolve(URL.urlparse(url
).netloc, rdtype = "AAAA"
).response.answer.pop(
).to_text(
).split(
).pop() ) )
except:
try:
addr = N4("{}/32".format(self.DNS.resolve(URL.urlparse(url
).netloc, rdtype = "A"
).response.answer.pop(
).to_text(
).split(
).pop() ) )
except Exception as ex:
self.l.exception(ex)
finally:
return(addr != None
and addr.is_global
and not addr.is_reserved
and not addr.is_multicast
and not addr.is_link_local
and not addr.is_private
and not addr.is_unspecified
and len([x.get("attrs"
)[ 0 ][ 1 ] for x in IP(
).get_addr() if x.get("attrs")[ 0 ][ 1 ] == addr.network_address]) == 0)
'''
Guesses the value type of a parameter (parameterization of calls to method calls from IRC via commands)
'''
def guess_type(self, s) -> type:
try:
value = AST.literal_eval(s)
except ValueError or SyntaxError:
self.l.debug("computed type {}".format(str) )
return str
else:
self.l.debug("computed type {}".format(type(value) ) )
return type(value)
'''
Sets up logging formatter and handler
'''
@staticmethod
def setup_logging(conf):
formatter = L.Formatter(
fmt = "%(levelname)s|%(module)s->%(funcName)s %(message)s" )
handler = L.StreamHandler(S.stderr)
handler.setFormatter(
formatter )
logger = L.getLogger()
if conf.log_level() == "debug":
logger.setLevel(L.DEBUG)
elif conf.log_level() == "info":
logger.setLevel(L.INFO)
logger.addHandler(handler)
return logger
'''
Gives information about an expanded CIDR prefix
'''
def cmd_cidr(self, event) -> None:
[ event.bot().connection.privmsg(event.target(), x) for x in map(lambda c: '''
{prefix}
{_s_} {_s_} {bold} size: {reset}
{_s_}{underline} {num_addresses} {reset} {_s_} {bold} start: {reset}
{_s_}{underline} {start} {reset} {_s_} {bold} end: {reset}
{_s_}{underline} {end} {reset} {_s_} {bold} netmask: {reset}
{_s_}{underline} {netmask} {reset} {_s_} {bold} global: {reset}
{_s_}{underline} {_global} {reset} {_s_} {bold} private: {reset}
{_s_}{underline} {private} {reset} {_s_} {bold} link-local: {reset}
{_s_}{underline} {link_local} {reset} {_s_} {bold} multicast: {reset}
{_s_}{underline} {multicast} {reset} {_s_} {bold} unspecified: {reset}
{_s_}{underline} {unspecified} {reset} {_s_} {bold} supernet: {reset}
{_s_}{underline} {supernet} {reset}
'''.replace(
"\n",
""
).replace(
" ",
""
).strip(
" "
).format(
_s_ = " ",
bold = IRC_TXT.IRC_FMT_BOLD,
underline = IRC_TXT.IRC_FMT_UNDERLINE,
reset = IRC_TXT.IRC_FMT_RESET,
prefix = IRC_TXT.meander,
num_addresses = c.num_addresses,
start = c.network_address.exploded,
end = c.broadcast_address,
netmask = c.with_netmask,
_global = c.is_global,
private = c.is_private,
link_local = c.is_link_local,
multicast = c.is_multicast,
unspecified = c.is_unspecified,
supernet = c.supernet()
), [ x.find(":") != -1
and N6(x)
or N4(x)
for x in event.event_arguments() ] ) ]
'''
Resolve DNS for a hostname
'''
def cmd_dns(self, event) -> None:
self.l.debug(event)
args = event.event_arguments()
rdtype = (len(args) > 1) and args[ 0 ] or "A"
Q = (len(args) > 1) and args[ 1 ] or args[ 0 ]
answer = self.DNS.resolve(
rdtype = rdtype,
qname = Q)
event.bot().connection.privmsg(
event.target(),
"{prefix} {bold}{c_name}{reset} @{name_server} rrset: {rr_set} ".format(
prefix = IRC_TXT.meander,
bold = IRC_TXT.IRC_FMT_BOLD,
reset = IRC_TXT.IRC_FMT_RESET,
c_name = answer.canonical_name,
name_server = answer.nameserver,
rr_set = answer.rrset.to_text() ) )
UTIL = utilities()