Add twitch support #6

Merged
skeh merged 3 commits from provider/twitch into main 2021-04-08 07:13:28 +00:00
6 changed files with 253 additions and 263 deletions

View File

@ -6,9 +6,8 @@ name = "pypi"
[packages] [packages]
requests = "*" requests = "*"
pygame = "*" pygame = "*"
websocket-client = "*"
[dev-packages] miniirc = "*"
python-language-server = {extras = ["all"], version = "*"}
[requires] [requires]
python_version = "3.9" python_version = "3.9"

326
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "659dc7baae3e246c60460dd8167ae448e6980ede9ccb2b10b2d6e0b995d73eeb" "sha256": "c6f4727f4fbf44c5155fd9276c2a575224bd4ce1914e7051b84c01dea7168336"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -18,17 +18,18 @@
"default": { "default": {
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd", "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
"sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4" "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
], ],
"version": "==2020.11.8" "version": "==2020.12.5"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
], ],
"version": "==3.0.4" "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
@ -38,209 +39,68 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10" "version": "==2.10"
}, },
"pygame": { "miniirc": {
"hashes": [ "hashes": [
"sha256:00268b75204519628760030d256b17e557710a859fadf7cbf80ead80efc22779", "sha256:3734fde677785fd1f735a1710b94b93643f6ecd769b8b41c084ef42515a70c47",
"sha256:0ba624e8cc2056f0c8f7f819f2564284bb675dd9a8097e03762763a63f99e1b3", "sha256:7be5d125b02275508b65421de91af6a2e3efeac0f5b7708454ddd2cbac9a5250"
"sha256:0fa2dfa9daacaa5533ab733c0fd038e319b83d685f92e986fe3233dd06e508e4",
"sha256:19b66c7546261996e2533b922f78b04edc1901586d6e023af7ec0aef4d6d4a9f",
"sha256:1bc6e76e31e7ce6b734f12a06d975af2ef29da93e5ccbecbc8ed2d38d1bea8ea",
"sha256:208fbc0f25a641b953c7ec1c537a3c0b43a4faf6a8f303be15d02e308c671bab",
"sha256:21f4e50c1f13e143c590a48e63644ed2d0cfb50f9ce699c5e8b7af7caaa5cbe3",
"sha256:2c2475f706e49b4a67d6958e5cce1157f3875083eba92ab298bdf7ba33780524",
"sha256:3a2e7a0e6377224d3956ac9791382f7baadf66957679be8a9cc493f488245584",
"sha256:3d0be0275b1d4939c9c9074d879407f330cbac0c837cd0f4b24ab0377959f6da",
"sha256:411fa087a21f065d720d199b848652bf02d61cb39ff4d62745be37257fd84361",
"sha256:4e58cc6f47412cf441a0c9b98dcb8141e1f2cdb6bbd0b00d1b89572d54195002",
"sha256:5450704eab972951cce02231e21dbbdffcf7e7c569cfb8d26346e2b18197ae0f",
"sha256:579d2162591241dc98c80c796576bb93edbd8107a8a43e13a18bf3c9c0dfb446",
"sha256:5a3f0f436174e95027c60a37f06d84d25d2275de3255fe85846baa47c0e36e6b",
"sha256:5ed4b00612b11c1ebbfbff1379616de2fa4d1acd9830232cc9a4a2ac2aa55e84",
"sha256:63b038da116a643046181b02173fd894d87d2f85ecfd6aa7d5ece73c6ef501e9",
"sha256:697599838b559b9f6573c3064357b404d3fd7af145168e2f4e3ed551bc4400ec",
"sha256:69f9452365eef5ef476eca2657dcc488d8f93423a2f6e80e6dc5ada9fa813d21",
"sha256:6bd16df20e5b9bc82a4dae7342573a7917530c2e4713c576f2684ebc79a9aa73",
"sha256:6f5b10de20a6d6a4c84371fd55d25038ef3fa3ceb1a20a61bcab1b1f2459a115",
"sha256:7c228e067b5e212acc2ee1412e2cf29f5900fd290b09556014aee74665ffaded",
"sha256:7ee08691262b1a22dfd28b9b0e126cfc0276b349ddfd7ef8964a0e54d000ab0e",
"sha256:87795c07c1de05150759b52e7bc3ccb38e76148ae8e40f07bf2b1368e7fabf0e",
"sha256:8a240f598c21a4d2218eaeccfdd77bf0d0f6789133a154b07667285157b95ffa",
"sha256:9d10a197518f8b50d3a61b923762a37063b9d6e27d1abe5167bb3a762e816833",
"sha256:a03f0357ad4a9daf6e70b239804b77088d0b1543fc32e672f97f38e35fb1c206",
"sha256:a7d298ebc7239dcf98e093b3510abc5279c8c7c149c60d2a8a81a1b44a671da3",
"sha256:abed99f21c11d7f8deb0b3a41e2e4b0d00a98c17c23edd4dd344f169b317a586",
"sha256:b5ac39ef5453252cb4b4ce2956ad9832688476a7a99bbdee1a7e17bf40e0a11f",
"sha256:c2aea8cfa8156016d5316a601e804e8eacc77facd550d6bff893130f8f506a45",
"sha256:cb6dda696df8326eebf8514aa7d6e18977d37822241d8661069ca42a45f957d6",
"sha256:d1b9279133bb9f1ff5e7f5369a7a25749279724f64428980f6cec58eb7ce18da",
"sha256:d700ffe744efde7eba1709950066429bdd21e9eec2288e29d39c3a9f21a04a7c",
"sha256:d768ff1f2dd3de84585a68b491821badc35b2c9c4453c6cbcf48949be99ea781",
"sha256:db05ce075aed6a9ac6b5613f273eb134a643a7b910ded4f6395b09a7f24edacf",
"sha256:dc7cbfc0a0a0f1f8b1be4dbf0788a4ac7d3dd2991067e1cfd869c65c323a24a5",
"sha256:e21e68b1110bea64ed1dee6e644b49423f9539fb3e29d27ca6c68011f999c8f9",
"sha256:e716b3a5ca469959ad0d631cbb7846386741fc056968a3abb097ba5b43757214",
"sha256:e75fe7624d2fc03b93a706f4d8f8204cb9c83c8f16245553546b3bd0a733556c",
"sha256:e8effa8b5a8d2bb9c07dccb18337b07174d4956516978a35350bc23e254189cc"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.0.0" "version": "==1.6.3"
},
"pygame": {
"hashes": [
"sha256:0571dde0277483f5060c8ee43cbfd8df5776b12505e3948eee241c8ce9b93371",
"sha256:07ed57062be4bb9741f57dab1751d95574c091c9958ed7e39cdb246d50903283",
"sha256:107d5f82f471baee4b9522a691cb320dd52dbf329ed7a0e9ab25f75cd3caf890",
"sha256:10ca736eecedadf492ba1191f9fa3a5e6f30db2b9f8882b3ee7706d5a89c14e0",
"sha256:19357c826ab94f9ae5b4ec5cb752cc806cfc29ea32cf7bdaacb65fa2615607e8",
"sha256:21475405bcdeb20b8a796a3da6704ebb816e06b29749dd64ff619e80816b7932",
"sha256:2d9b0a66034fed390ee367a549435853502c9d4fe82ac0fa3a520f0ad5648e6e",
"sha256:30eb5c7adb0b3362024cec2c461be6978fbfc99c3bca974e438b1b540cd09438",
"sha256:3270cbcff40ca2b5622a145346298a33285c91b6d50097e0b85123d9a2bc7c9b",
"sha256:328d70d40bc9a6defb9f330f5e7f3d0726af1e7c2308ebca582e69480db2950d",
"sha256:3b31d088129977885f72037c55cfa1140e9bcf3468e68b46141f6cc2b33d456b",
"sha256:3c2676b4fd278d632037eacd3b0524ce1a592c048e8e5eb5830475f83585cb3a",
"sha256:44f3ff8224d7cb998642400371c685005c8316b55e87794cbf1f6407b88ec424",
"sha256:49c2f58559c1fbf4ba258e4b141578ccb0e83da3d4f823894f6171a8f0d594ed",
"sha256:4d3135a1f8c76c3fff1ef8b7a51e4c6523748e9bdbd7bca6daa69790ce0e798a",
"sha256:4f41252dfa1e8bb95f2ea51fba710827dde9820a535623d002a65621bafe7e3f",
"sha256:5aeeb6659a7fe7760a78e449566553ae8c949ae29dd907a8eb4171fa0a274c16",
"sha256:5afc34f0af0cec09a20b6bb09090054fac5169ab01909e01b06e7e0752ab0153",
"sha256:5f057e5aa4c383fcf18560dcae2c5593e37e3fc941083a0a00a17f7cf25ee522",
"sha256:72625dc949c6d08ba7ce7c37a33163bb498d90ca0d7e626db3cfbf486df4db1d",
"sha256:7ea518d8eeb072c77c16977cdee3c59d9fffa750ed9c7c9c533ba520b6b08af7",
"sha256:81510ffb1c31a3827c6be047b1926d81caf36dc734564ca0e14903d6bce60c6f",
"sha256:845385caf99f8d941607791c60e560d24b4a35c70eef0b01c30cfde0b913ff92",
"sha256:898874521a9be1f9dbc5b036a9755803287c2664e335afd3e10963f7f4ccb853",
"sha256:8b1e7b63f47aafcdd8849933b206778747ef1802bd3d526aca45ed77141e4001",
"sha256:9aead3f2eed90260136b201f398965900c5335c974bb7b47c381d98e39284018",
"sha256:9de559462aaa68c40bb7625dcd587584b4eb85c4208528dc97b9ee7254945294",
"sha256:9f48277de1daa83fd58a722b2e3423201b5eb39842227f32702fb78e4bba5a71",
"sha256:9fd0691c4fe58b932674bb6a91d2808790e8269c3183ef16052f13e1c602ac00",
"sha256:a4e35d89b6754941e82df1ce980a1c370943d3c076938d94ed1e48165dd6a11b",
"sha256:ad230911d61f448c09886d3c92b2eae44ca7530babe9c48e74e02a0622ce2d34",
"sha256:b812285d23b5644c643a6ae30553a772f935f47f61826660b108b8727936384b",
"sha256:bf833c853a0568738ee5d88e1345c17bf3e8db626c36fb895327a35bb1827b0b",
"sha256:c188ce4bf1544f2758e8b651f4349a0f3dc441e09d8ab7c4863db1ae8f084a32",
"sha256:d8059084ce54b2c3d7b2c8bacd5f6490db849b2d2d6e7368c160b08504c87e73",
"sha256:e72cdc97a49509ca2298350c2c3a0ac26bc8e943ce003a7d245df42e91439d5d",
"sha256:eab18df58dcc8512f1b694f7218146828d7e3dd3f4e73bfd6942a11810293fd5",
"sha256:ece424c83a575c2e0ba25815871458d3bbade46d76b7997236fb51a0251229ab",
"sha256:ed80b40da839d60f4c03915bb3638e3c96ea8c30e689d0cc309b7597d82cc217",
"sha256:fd5ee0f42d59a290c049f91894e0739f62c2908e7edc028ffb847a105e68bfc3",
"sha256:fd6acd09d2a0fd3f616b18f977f399ed3dd95e2d6754f115837f026d19d62e10"
],
"index": "pypi",
"version": "==2.0.1"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.25.0" "version": "==2.25.1"
},
"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"
}
},
"develop": {
"astroid": {
"hashes": [
"sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703",
"sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"
],
"markers": "python_version >= '3.5'",
"version": "==2.4.2"
},
"autopep8": {
"hashes": [
"sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094"
],
"version": "==1.5.4"
},
"flake8": {
"hashes": [
"sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839",
"sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"
],
"version": "==3.8.4"
},
"isort": {
"hashes": [
"sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7",
"sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"
],
"markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==5.6.4"
},
"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"
},
"lazy-object-proxy": {
"hashes": [
"sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
"sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
"sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
"sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
"sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
"sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
"sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
"sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
"sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
"sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
"sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
"sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
"sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
"sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
"sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
"sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
"sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
"sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
"sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
"sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
"sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.4.3"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"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"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"pycodestyle": {
"hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
"version": "==2.6.0"
},
"pydocstyle": {
"hashes": [
"sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325",
"sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"
],
"version": "==5.1.1"
},
"pyflakes": {
"hashes": [
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
],
"version": "==2.2.0"
},
"pylint": {
"hashes": [
"sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210",
"sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"
],
"version": "==2.6.0"
},
"python-jsonrpc-server": {
"hashes": [
"sha256:62c543e541f101ec5b57dc654efc212d2c2e3ea47ff6f54b2e7dcb36ecf20595",
"sha256:e5a908ff182e620aac07db5f57887eeb0afe33993008f57dc1b85b594cea250c"
],
"version": "==0.4.0"
},
"python-language-server": {
"extras": [
"all"
],
"hashes": [
"sha256:0c672aec88fa73f47c36aa4d0b7717868b316a1398d3fb73554955675ce54bae",
"sha256:c85d718ef6860319ad59fd6f2acb1166e9349b782ee8e8908e08ecf241615f52"
],
"index": "pypi",
"version": "==0.36.1"
},
"rope": {
"hashes": [
"sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04"
],
"version": "==0.18.0"
}, },
"six": { "six": {
"hashes": [ "hashes": [
@ -250,60 +110,22 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0" "version": "==1.15.0"
}, },
"snowballstemmer": { "urllib3": {
"hashes": [ "hashes": [
"sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
"sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
], ],
"version": "==2.0.0" "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.4"
}, },
"toml": { "websocket-client": {
"hashes": [ "hashes": [
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" "sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "index": "pypi",
"version": "==0.10.2" "version": "==0.58.0"
},
"ujson": {
"hashes": [
"sha256:078808c385036cba73cad96f498310c61e9b5ae5ac9ea01e7c3996ece544b556",
"sha256:0a2e1b211714eb1ec0772a013ec9967f8f95f21c84e8f46382e9f8a32ae781fe",
"sha256:0f412c3f59b1ab0f40018235224ca0cf29232d0201ff5085618565a8a9c810ed",
"sha256:26cf6241b36ff5ce4539ae687b6b02673109c5e3efc96148806a7873eaa229d3",
"sha256:2b2d9264ac76aeb11f590f7a1ccff0689ba1313adacbb6d38d3b15f21a392897",
"sha256:4f12b0b4e235b35d49f15227b0a827e614c52dda903c58a8f5523936c233dfc7",
"sha256:4fe8c6112b732cba5a722f7cbe22f18d405f6f44415794a5b46473a477635233",
"sha256:51480048373cf97a6b97fcd70c3586ca0a31f27e22ab680fb14c1f22bedbf743",
"sha256:568bb3e7f035006147af4ce3a9ced7d126c92e1a8607c7b2266007b1c1162c53",
"sha256:5fe1536465b1c86e32a47113abd3178001b7c2dcd61f95f336fe2febf4661e74",
"sha256:71703a269f074ff65b9d7746662e4b3e76a4af443e532218af1e8ce15d9b1e7b",
"sha256:7a1545ac2476db4cc1f0f236603ccbb50991fc1bba480cda1bc06348cc2a2bf0",
"sha256:a5200a68f1dcf3ce275e1cefbcfa3914b70c2b5e2f71c2e31556aa1f7244c845",
"sha256:a618af22407baeadb3f046f81e7a5ee5e9f8b0b716d2b564f92276a54d26a823",
"sha256:a79bca47eafb31c74b38e68623bc9b2bb930cb48fab1af31c8f2cb68cf473421",
"sha256:b87379a3f8046d6d111762d81f3384bf38ab24b1535c841fe867a4a097d84523",
"sha256:bd4c77aee3ffb920e2dbc21a9e0c7945a400557ce671cfd57dbd569f5ebc619d",
"sha256:c354c1617b0a4378b6279d0cd511b769500cf3fa7c42e8e004cbbbb6b4c2a875",
"sha256:c604024bd853b5df6be7d933e934da8dd139e6159564db7c55b92a9937678093",
"sha256:e7ab24942b2d57920d75b817b8eead293026db003247e26f99506bdad86c61b4",
"sha256:f8a60928737a9a47e692fcd661ef2b5d75ba22c7c930025bd95e338f2a6e15bc"
],
"markers": "python_version >= '3.1'",
"version": "==4.0.1"
},
"wrapt": {
"hashes": [
"sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"
],
"version": "==1.12.1"
},
"yapf": {
"hashes": [
"sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427",
"sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"
],
"version": "==0.30.0"
} }
} },
"develop": {}
} }

View File

@ -1,6 +1,6 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from multiprocessing import Process, Pipe, Queue from multiprocessing import Process, Pipe, Queue
from aenum import Enum, auto from enum import Enum, auto
from traceback import format_exception from traceback import format_exception
import time import time
@ -40,10 +40,6 @@ class ChatProcess(Process, ABC):
def control_pipe(self): def control_pipe(self):
return self._caller_pipe return self._caller_pipe
@property
def deamon(self):
return True
@property @property
def state(self): def state(self):
pass pass

142
chats/Twitch.py Normal file
View File

@ -0,0 +1,142 @@
import websocket
from multiprocessing import Process, Pipe
from enum import Enum, auto
from traceback import format_exception
from miniirc import ircv3_message_parser
from . import AUTHOR_TYPES
from .ChatProcess import ChatProcess
class CONTROL_MESSAGES(Enum):
RESTART = auto()
class STATES(Enum):
DISCONNECTED = auto()
READING = auto()
FAILURE = auto()
class NonBlockingWebsocket(Process):
def __init__(self):
super().__init__()
self._ws = websocket.WebSocket()
self._pipe, self._caller_pipe = Pipe()
def send(self, data):
return self._ws.send(data)
def poll(self, *args):
return self._caller_pipe.poll(*args)
def recv(self, *args):
return self._caller_pipe.recv(*args)
def connect(self, *args):
self._ws.connect(*args)
def run(self):
try:
while self.is_alive():
data = self._ws.recv()
self._pipe.send(data)
except Exception as e:
print(f'Failure in {self.__class__.__name__}: {e}')
print(''.join(format_exception(None, e, e.__traceback__)))
self._caller_pipe.close()
return -1
class TwitchIRCProcess(ChatProcess):
WEBSOCKET_ADDRESS = 'wss://irc-ws.chat.twitch.tv:443'
def __init__(self, username, token, *args):
super().__init__(*args)
self._state_machine = self.bind_to_states(STATES)
self._username = username
self._token = token
self.state = STATES.DISCONNECTED
@property
def keybinds(self):
return {
ord('r'): {'type': CONTROL_MESSAGES.RESTART}
}
def loop(self, next_state):
return self._state_machine(self.state, next_state)
def on_state_enter(self, new_state):
status_messages = {
STATES.READING: 'Connected to channel!',
STATES.FAILURE: 'Connection closed - see terminal',
}
message = status_messages.get(new_state)
if message is not None:
self._message_queue.put({
'text': message,
'author_name': 'Twitch IRC Bot', 'author_id': self.__class__.__name__, 'author_type': AUTHOR_TYPES.SYSTEM,
})
def on_disconnected(self, next_state):
self._ws = NonBlockingWebsocket()
self._ws.connect(TwitchIRCProcess.WEBSOCKET_ADDRESS)
self._ws.start()
self._ws.send(f'PASS {self._token}')
self._ws.send(f'NICK {self._username}')
response = self._ws.recv()
if any('Welcome, GLHF!' in msg for msg in response.splitlines()):
self._ws.send(f'JOIN #{self._username.lower()}')
self._ws.send('CAP REQ :twitch.tv/tags')
# Not currently using the data we get back from these, so leaving it to READING state to ignore
return STATES.READING
else:
print(response)
return STATES.FAILURE
def on_reading(self, next_state):
if self._ws.poll(0.1):
messages = self._ws.recv()
for message in messages.splitlines():
cmd, hostmask, tags, args = ircv3_message_parser(message)
if cmd == 'PRIVMSG':
normalized_message = TwitchIRCProcess.normalize_message(hostmask, tags, args)
self._message_queue.put(normalized_message)
elif cmd == 'PING':
self._ws.send(f"PONG {' '.join(args)}")
else:
# HACK: ignoring quite a lot of messages rn
pass
return 0
def on_failure(self, next_state):
self._ws.terminate()
return None
@classmethod
def normalize_message(cls, hostmask, tags, args):
badges = [badge.split('/')[0] for badge in tags.get('badges', '').split(',')]
# First arg is the room (which we dont care about, since it should only ever be #user)
# First character of the second arg is the data delimiter (:)
message = ' '.join(args[1:])[1:]
if 'broadcaster' in badges:
author_type = AUTHOR_TYPES.OWNER
elif any(target_badge in badges for target_badge in ['admin', 'moderator', 'global_mod']):
author_type = AUTHOR_TYPES.MODERATOR
elif 'subscriber' in badges:
author_type = AUTHOR_TYPES.PATRON
else:
author_type = AUTHOR_TYPES.USER
return {
'text': message,
'author_name': tags.get('display-name', hostmask[0]),
'author_id': tags.get('id'),
'author_type': author_type,
'author_color': tags.get('color'),
}

14
main.py
View File

@ -5,11 +5,14 @@ import time
from chats import YoutubeLive from chats import YoutubeLive
from chats.ChatProcess import LIFECYCLE_MESSAGES from chats.ChatProcess import LIFECYCLE_MESSAGES
from chats.FakeChat import FakeChat from chats.FakeChat import FakeChat
from chats.Twitch import TwitchIRCProcess
from ui.App import App from ui.App import App
from plugins.PogCount import PogCounter from plugins.PogCount import PogCounter
EVENT_POLL_FREQ = 0.1 EVENT_POLL_FREQ = 0.1
GOOGLE_API_SECRETS_PATH = 'googleapi.secret.json' GOOGLE_API_SECRETS_PATH = 'googleapi.secret.json'
TWITCH_SECRETS_PATH = 'twitch.secret.json'
if __name__ == '__main__': if __name__ == '__main__':
chat_processes = [FakeChat(EVENT_POLL_FREQ)] chat_processes = [FakeChat(EVENT_POLL_FREQ)]
@ -18,7 +21,16 @@ if __name__ == '__main__':
client_secrets = json.load(f)['installed'] client_secrets = json.load(f)['installed']
chat_processes.append(YoutubeLive.YoutubeLiveProcess(client_secrets, EVENT_POLL_FREQ)) chat_processes.append(YoutubeLive.YoutubeLiveProcess(client_secrets, EVENT_POLL_FREQ))
else: else:
print('No client secrets - disabling youtube chat client. Hit "f" to enable a testing client') print('No client secrets - disabling youtube chat client')
if os.path.exists(TWITCH_SECRETS_PATH):
with open(TWITCH_SECRETS_PATH ,'r') as f:
secrets = json.load(f)
chat_processes.append(TwitchIRCProcess(secrets.get('username'), secrets.get('oauth'), EVENT_POLL_FREQ))
else:
print('No twitch config - disabling twitch chat client')
if len(chat_processes) == 1:
print('Hit "f" with the window focused to enable a testing client')
plugins = [PogCounter('../live-status.txt', prefix='Pog count: ')] plugins = [PogCounter('../live-status.txt', prefix='Pog count: ')]

View File

@ -1,4 +1,5 @@
from hashlib import md5 from hashlib import md5
import re
import pygame import pygame
from pygame.locals import Rect from pygame.locals import Rect
@ -8,6 +9,20 @@ from chats import AUTHOR_TYPES
from . import BG_COLOR, XTERM_COLORS from . import BG_COLOR, XTERM_COLORS
from .TextFragment import TextFragment from .TextFragment import TextFragment
hex_color_regex = re.compile('^#[a-f0-9]{6}$')
def closest_xterm(target_color):
r, g, b = (int(target_color[i:i+2], 16) for i in range(1, len(target_color), 2))
distances = []
for xterm_color in XTERM_COLORS:
xr, xg, xb = xterm_color
distance = math.sqrt(abs(r - xr) ** 2 + abs(g - xg) ** 2 + abs(b - xb) ** 2)
if distance == 0:
return xterm_color
distances.append((distance, xterm_color))
return min(distances)[1]
class MessageView: class MessageView:
# TODO: Superchat and chat sponsor handling # TODO: Superchat and chat sponsor handling
@ -26,8 +41,12 @@ class MessageView:
author_id = message['author_id'] author_id = message['author_id']
author_type = message['author_type'] author_type = message['author_type']
author_hash = int(md5(author_id.encode('utf-8')).hexdigest(), 16) author_color = message.get('author_color')
author_color = (author_hash % 144) + 88 if author_color is not None and hex_color_regex.match(author_color):
author_color = closest_xterm(author_color)
else:
author_hash = int(md5(author_id.encode('utf-8')).hexdigest(), 16)
author_color = (author_hash % 144) + 88
tag_fg = XTERM_COLORS[15] tag_fg = XTERM_COLORS[15]
tag_bg = BG_COLOR tag_bg = BG_COLOR