├── .gitignore ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── Readme.md ├── img ├── img1.png ├── img2.png ├── img3.png ├── img4.png ├── img5.png └── img6.png ├── logbot ├── __init__.py ├── __main__.py ├── bot.py └── utils.py ├── makefile └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # IPython 79 | profile_default/ 80 | ipython_config.py 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # celery beat schedule file 86 | celerybeat-schedule 87 | 88 | # SageMath parsed files 89 | *.sage.py 90 | 91 | # Environments 92 | .env 93 | .venv 94 | env/ 95 | venv/ 96 | ENV/ 97 | env.bak/ 98 | venv.bak/ 99 | 100 | # Spyder project settings 101 | .spyderproject 102 | .spyproject 103 | 104 | # Rope project settings 105 | .ropeproject 106 | 107 | # mkdocs documentation 108 | /site 109 | 110 | # mypy 111 | .mypy_cache/ 112 | .dmypy.json 113 | dmypy.json 114 | 115 | # Pyre type checker 116 | .pyre/ 117 | 118 | .vscode/* 119 | # !.vscode/settings.json 120 | !.vscode/tasks.json 121 | !.vscode/launch.json 122 | !.vscode/extensions.json 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alejandro Piad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | pytest = "*" 8 | pytest-cov = "*" 9 | coveralls = "*" 10 | twine = "*" 11 | 12 | [packages] 13 | python-telegram-bot = "*" 14 | sanic = "*" 15 | emoji = "*" 16 | requests = "*" 17 | 18 | [requires] 19 | python_version = "3.6" 20 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "ef30c983ab7faab0f2dbdf1b2595909779a08180efbf0f8d4b8369e67cde1088" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.6" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "aiofiles": { 20 | "hashes": [ 21 | "sha256:021ea0ba314a86027c166ecc4b4c07f2d40fc0f4b3a950d1868a0f2571c2bbee", 22 | "sha256:1e644c2573f953664368de28d2aa4c89dfd64550429d0c27c4680ccd3aa4985d" 23 | ], 24 | "version": "==0.4.0" 25 | }, 26 | "certifi": { 27 | "hashes": [ 28 | "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", 29 | "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" 30 | ], 31 | "version": "==2019.9.11" 32 | }, 33 | "cffi": { 34 | "hashes": [ 35 | "sha256:00d890313797d9fe4420506613384b43099ad7d2b905c0752dbcc3a6f14d80fa", 36 | "sha256:0cf9e550ac6c5e57b713437e2f4ac2d7fd0cd10336525a27224f5fc1ec2ee59a", 37 | "sha256:0ea23c9c0cdd6778146a50d867d6405693ac3b80a68829966c98dd5e1bbae400", 38 | "sha256:193697c2918ecdb3865acf6557cddf5076bb39f1f654975e087b67efdff83365", 39 | "sha256:1ae14b542bf3b35e5229439c35653d2ef7d8316c1fffb980f9b7647e544baa98", 40 | "sha256:1e389e069450609c6ffa37f21f40cce36f9be7643bbe5051ab1de99d5a779526", 41 | "sha256:263242b6ace7f9cd4ea401428d2d45066b49a700852334fd55311bde36dcda14", 42 | "sha256:33142ae9807665fa6511cfa9857132b2c3ee6ddffb012b3f0933fc11e1e830d5", 43 | "sha256:364f8404034ae1b232335d8c7f7b57deac566f148f7222cef78cf8ae28ef764e", 44 | "sha256:47368f69fe6529f8f49a5d146ddee713fc9057e31d61e8b6dc86a6a5e38cecc1", 45 | "sha256:4895640844f17bec32943995dc8c96989226974dfeb9dd121cc45d36e0d0c434", 46 | "sha256:558b3afef987cf4b17abd849e7bedf64ee12b28175d564d05b628a0f9355599b", 47 | "sha256:5ba86e1d80d458b338bda676fd9f9d68cb4e7a03819632969cf6d46b01a26730", 48 | "sha256:63424daa6955e6b4c70dc2755897f5be1d719eabe71b2625948b222775ed5c43", 49 | "sha256:6381a7d8b1ebd0bc27c3bc85bc1bfadbb6e6f756b4d4db0aa1425c3719ba26b4", 50 | "sha256:6381ab708158c4e1639da1f2a7679a9bbe3e5a776fc6d1fd808076f0e3145331", 51 | "sha256:6fd58366747debfa5e6163ada468a90788411f10c92597d3b0a912d07e580c36", 52 | "sha256:728ec653964655d65408949b07f9b2219df78badd601d6c49e28d604efe40599", 53 | "sha256:7cfcfda59ef1f95b9f729c56fe8a4041899f96b72685d36ef16a3440a0f85da8", 54 | "sha256:819f8d5197c2684524637f940445c06e003c4a541f9983fd30d6deaa2a5487d8", 55 | "sha256:825ecffd9574557590e3225560a8a9d751f6ffe4a49e3c40918c9969b93395fa", 56 | "sha256:9009e917d8f5ef780c2626e29b6bc126f4cb2a4d43ca67aa2b40f2a5d6385e78", 57 | "sha256:9c77564a51d4d914ed5af096cd9843d90c45b784b511723bd46a8a9d09cf16fc", 58 | "sha256:a19089fa74ed19c4fe96502a291cfdb89223a9705b1d73b3005df4256976142e", 59 | "sha256:a40ed527bffa2b7ebe07acc5a3f782da072e262ca994b4f2085100b5a444bbb2", 60 | "sha256:bb75ba21d5716abc41af16eac1145ab2e471deedde1f22c6f99bd9f995504df0", 61 | "sha256:e22a00c0c81ffcecaf07c2bfb3672fa372c50e2bd1024ffee0da191c1b27fc71", 62 | "sha256:e55b5a746fb77f10c83e8af081979351722f6ea48facea79d470b3731c7b2891", 63 | "sha256:ec2fa3ee81707a5232bf2dfbd6623fdb278e070d596effc7e2d788f2ada71a05", 64 | "sha256:fd82eb4694be712fcae03c717ca2e0fc720657ac226b80bbb597e971fc6928c2" 65 | ], 66 | "version": "==1.13.1" 67 | }, 68 | "chardet": { 69 | "hashes": [ 70 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 71 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 72 | ], 73 | "version": "==3.0.4" 74 | }, 75 | "cryptography": { 76 | "hashes": [ 77 | "sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c", 78 | "sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595", 79 | "sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad", 80 | "sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651", 81 | "sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2", 82 | "sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff", 83 | "sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d", 84 | "sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42", 85 | "sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d", 86 | "sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e", 87 | "sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912", 88 | "sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793", 89 | "sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13", 90 | "sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7", 91 | "sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0", 92 | "sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879", 93 | "sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f", 94 | "sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9", 95 | "sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2", 96 | "sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf", 97 | "sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8" 98 | ], 99 | "version": "==2.8" 100 | }, 101 | "emoji": { 102 | "hashes": [ 103 | "sha256:1e959336dafc7a5ed2c0256ee587bbd38a7187d772141f0b5ba42de9e08599a8", 104 | "sha256:a9e9c08be9907c0042212c86dfbea0f61f78e9897d4df41a1d6307017763ad3e" 105 | ], 106 | "index": "pypi", 107 | "version": "==0.5.1" 108 | }, 109 | "future": { 110 | "hashes": [ 111 | "sha256:858e38522e8fd0d3ce8f0c1feaf0603358e366d5403209674c7b617fa0c24093" 112 | ], 113 | "version": "==0.18.1" 114 | }, 115 | "httptools": { 116 | "hashes": [ 117 | "sha256:e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc" 118 | ], 119 | "version": "==0.0.13" 120 | }, 121 | "idna": { 122 | "hashes": [ 123 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 124 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 125 | ], 126 | "version": "==2.8" 127 | }, 128 | "multidict": { 129 | "hashes": [ 130 | "sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f", 131 | "sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3", 132 | "sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef", 133 | "sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b", 134 | "sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73", 135 | "sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc", 136 | "sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3", 137 | "sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd", 138 | "sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351", 139 | "sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941", 140 | "sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d", 141 | "sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1", 142 | "sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b", 143 | "sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a", 144 | "sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3", 145 | "sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7", 146 | "sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0", 147 | "sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0", 148 | "sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014", 149 | "sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5", 150 | "sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036", 151 | "sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d", 152 | "sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a", 153 | "sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce", 154 | "sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1", 155 | "sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a", 156 | "sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9", 157 | "sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7", 158 | "sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b" 159 | ], 160 | "version": "==4.5.2" 161 | }, 162 | "pycparser": { 163 | "hashes": [ 164 | "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" 165 | ], 166 | "version": "==2.19" 167 | }, 168 | "python-telegram-bot": { 169 | "hashes": [ 170 | "sha256:78695b1f6e147e9b360ccfb1ac92b542cab27870ccaf04065a88ee601ffa58b6", 171 | "sha256:cca4e32ebb8da7fdf35ab2fa2b3edd441211364819c5592fc253acdb7561ea5b" 172 | ], 173 | "index": "pypi", 174 | "version": "==11.1.0" 175 | }, 176 | "requests": { 177 | "hashes": [ 178 | "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", 179 | "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" 180 | ], 181 | "index": "pypi", 182 | "version": "==2.21.0" 183 | }, 184 | "sanic": { 185 | "hashes": [ 186 | "sha256:4ce5ffd9588557caff844da0f4b478d263d0e836fa0204480a2a17958b333fe0", 187 | "sha256:e1f291cfc44627baad7e0855512c821fe3dbfe23f7d2a26561abb808d4511f14" 188 | ], 189 | "index": "pypi", 190 | "version": "==18.12.0" 191 | }, 192 | "six": { 193 | "hashes": [ 194 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 195 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 196 | ], 197 | "version": "==1.12.0" 198 | }, 199 | "ujson": { 200 | "hashes": [ 201 | "sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86" 202 | ], 203 | "markers": "sys_platform != 'win32' and implementation_name == 'cpython'", 204 | "version": "==1.35" 205 | }, 206 | "urllib3": { 207 | "hashes": [ 208 | "sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0", 209 | "sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3" 210 | ], 211 | "index": "pypi", 212 | "version": "==1.24.2" 213 | }, 214 | "uvloop": { 215 | "hashes": [ 216 | "sha256:0deb6c97c5807c792dd9024bab90e6ca49e981862103cb2ea37b430c1ca0a267", 217 | "sha256:155b34d513655e753d07f499a7e811970e2d397f240dfcbec0b32a9587159c99", 218 | "sha256:1df3ddfa280206e9999ae1c777a20836eb895bcec6dc9fae2cbb6eecfafb099e", 219 | "sha256:5b19361c8767e1dc61f6367f948d4f3dc5504b9f2eba488641b3d26ec14498ba", 220 | "sha256:942cd07035510b149d6160796f4e972137130ae953871b6a98c2cf5d5ab68c2e", 221 | "sha256:c63b6c0bf33144c604dd72f7eecf2d3a3ac7405c503c67bd98128cf306efe18a", 222 | "sha256:e698a20a3b4ccb380d207f9d491d4085d7c38d364f6a0bae98684a1612a9607a" 223 | ], 224 | "markers": "sys_platform != 'win32' and implementation_name == 'cpython'", 225 | "version": "==0.13.0" 226 | }, 227 | "websockets": { 228 | "hashes": [ 229 | "sha256:0e2f7d6567838369af074f0ef4d0b802d19fa1fee135d864acc656ceefa33136", 230 | "sha256:2a16dac282b2fdae75178d0ed3d5b9bc3258dabfae50196cbb30578d84b6f6a6", 231 | "sha256:5a1fa6072405648cb5b3688e9ed3b94be683ce4a4e5723e6f5d34859dee495c1", 232 | "sha256:5c1f55a1274df9d6a37553fef8cff2958515438c58920897675c9bc70f5a0538", 233 | "sha256:669d1e46f165e0ad152ed8197f7edead22854a6c90419f544e0f234cc9dac6c4", 234 | "sha256:695e34c4dbea18d09ab2c258994a8bf6a09564e762655408241f6a14592d2908", 235 | "sha256:6b2e03d69afa8d20253455e67b64de1a82ff8612db105113cccec35d3f8429f0", 236 | "sha256:79ca7cdda7ad4e3663ea3c43bfa8637fc5d5604c7737f19a8964781abbd1148d", 237 | "sha256:7fd2dd9a856f72e6ed06f82facfce01d119b88457cd4b47b7ae501e8e11eba9c", 238 | "sha256:82c0354ac39379d836719a77ee360ef865377aa6fdead87909d50248d0f05f4d", 239 | "sha256:8f3b956d11c5b301206382726210dc1d3bee1a9ccf7aadf895aaf31f71c3716c", 240 | "sha256:91ec98640220ae05b34b79ee88abf27f97ef7c61cf525eec57ea8fcea9f7dddb", 241 | "sha256:952be9540d83dba815569d5cb5f31708801e0bbfc3a8c5aef1890b57ed7e58bf", 242 | "sha256:99ac266af38ba1b1fe13975aea01ac0e14bb5f3a3200d2c69f05385768b8568e", 243 | "sha256:9fa122e7adb24232247f8a89f2d9070bf64b7869daf93ac5e19546b409e47e96", 244 | "sha256:a0873eadc4b8ca93e2e848d490809e0123eea154aa44ecd0109c4d0171869584", 245 | "sha256:cb998bd4d93af46b8b49ecf5a72c0a98e5cc6d57fdca6527ba78ad89d6606484", 246 | "sha256:e02e57346f6a68523e3c43bbdf35dde5c440318d1f827208ae455f6a2ace446d", 247 | "sha256:e79a5a896bcee7fff24a788d72e5c69f13e61369d055f28113e71945a7eb1559", 248 | "sha256:ee55eb6bcf23ecc975e6b47c127c201b913598f38b6a300075f84eeef2d3baff", 249 | "sha256:f1414e6cbcea8d22843e7eafdfdfae3dd1aba41d1945f6ca66e4806c07c4f454" 250 | ], 251 | "version": "==6.0" 252 | } 253 | }, 254 | "develop": { 255 | "atomicwrites": { 256 | "hashes": [ 257 | "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", 258 | "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" 259 | ], 260 | "version": "==1.3.0" 261 | }, 262 | "attrs": { 263 | "hashes": [ 264 | "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", 265 | "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" 266 | ], 267 | "version": "==19.3.0" 268 | }, 269 | "bleach": { 270 | "hashes": [ 271 | "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", 272 | "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa" 273 | ], 274 | "version": "==3.1.0" 275 | }, 276 | "certifi": { 277 | "hashes": [ 278 | "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", 279 | "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" 280 | ], 281 | "version": "==2019.9.11" 282 | }, 283 | "chardet": { 284 | "hashes": [ 285 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 286 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 287 | ], 288 | "version": "==3.0.4" 289 | }, 290 | "coverage": { 291 | "hashes": [ 292 | "sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", 293 | "sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650", 294 | "sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5", 295 | "sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d", 296 | "sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351", 297 | "sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755", 298 | "sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef", 299 | "sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca", 300 | "sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca", 301 | "sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9", 302 | "sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc", 303 | "sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5", 304 | "sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f", 305 | "sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe", 306 | "sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888", 307 | "sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5", 308 | "sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce", 309 | "sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5", 310 | "sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e", 311 | "sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e", 312 | "sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9", 313 | "sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437", 314 | "sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1", 315 | "sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c", 316 | "sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24", 317 | "sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47", 318 | "sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2", 319 | "sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28", 320 | "sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c", 321 | "sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7", 322 | "sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0", 323 | "sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025" 324 | ], 325 | "version": "==4.5.4" 326 | }, 327 | "coveralls": { 328 | "hashes": [ 329 | "sha256:ab638e88d38916a6cedbf80a9cd8992d5fa55c77ab755e262e00b36792b7cd6d", 330 | "sha256:b2388747e2529fa4c669fb1e3e2756e4e07b6ee56c7d9fce05f35ccccc913aa0" 331 | ], 332 | "index": "pypi", 333 | "version": "==1.5.1" 334 | }, 335 | "docopt": { 336 | "hashes": [ 337 | "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" 338 | ], 339 | "version": "==0.6.2" 340 | }, 341 | "docutils": { 342 | "hashes": [ 343 | "sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0", 344 | "sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827", 345 | "sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99" 346 | ], 347 | "version": "==0.15.2" 348 | }, 349 | "idna": { 350 | "hashes": [ 351 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 352 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 353 | ], 354 | "version": "==2.8" 355 | }, 356 | "importlib-metadata": { 357 | "hashes": [ 358 | "sha256:aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", 359 | "sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af" 360 | ], 361 | "markers": "python_version < '3.8'", 362 | "version": "==0.23" 363 | }, 364 | "more-itertools": { 365 | "hashes": [ 366 | "sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", 367 | "sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4" 368 | ], 369 | "version": "==7.2.0" 370 | }, 371 | "pkginfo": { 372 | "hashes": [ 373 | "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb", 374 | "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32" 375 | ], 376 | "version": "==1.5.0.1" 377 | }, 378 | "pluggy": { 379 | "hashes": [ 380 | "sha256:0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", 381 | "sha256:fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34" 382 | ], 383 | "version": "==0.13.0" 384 | }, 385 | "py": { 386 | "hashes": [ 387 | "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", 388 | "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" 389 | ], 390 | "version": "==1.8.0" 391 | }, 392 | "pygments": { 393 | "hashes": [ 394 | "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", 395 | "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" 396 | ], 397 | "version": "==2.4.2" 398 | }, 399 | "pytest": { 400 | "hashes": [ 401 | "sha256:65aeaa77ae87c7fc95de56285282546cfa9c886dc8e5dc78313db1c25e21bc07", 402 | "sha256:6ac6d467d9f053e95aaacd79f831dbecfe730f419c6c7022cb316b365cd9199d" 403 | ], 404 | "index": "pypi", 405 | "version": "==4.2.0" 406 | }, 407 | "pytest-cov": { 408 | "hashes": [ 409 | "sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33", 410 | "sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f" 411 | ], 412 | "index": "pypi", 413 | "version": "==2.6.1" 414 | }, 415 | "readme-renderer": { 416 | "hashes": [ 417 | "sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f", 418 | "sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d" 419 | ], 420 | "version": "==24.0" 421 | }, 422 | "requests": { 423 | "hashes": [ 424 | "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", 425 | "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" 426 | ], 427 | "index": "pypi", 428 | "version": "==2.21.0" 429 | }, 430 | "requests-toolbelt": { 431 | "hashes": [ 432 | "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", 433 | "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" 434 | ], 435 | "version": "==0.9.1" 436 | }, 437 | "six": { 438 | "hashes": [ 439 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 440 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 441 | ], 442 | "version": "==1.12.0" 443 | }, 444 | "tqdm": { 445 | "hashes": [ 446 | "sha256:abc25d0ce2397d070ef07d8c7e706aede7920da163c64997585d42d3537ece3d", 447 | "sha256:dd3fcca8488bb1d416aa7469d2f277902f26260c45aa86b667b074cd44b3b115" 448 | ], 449 | "version": "==4.36.1" 450 | }, 451 | "twine": { 452 | "hashes": [ 453 | "sha256:7d89bc6acafb31d124e6e5b295ef26ac77030bf098960c2a4c4e058335827c5c", 454 | "sha256:fad6f1251195f7ddd1460cb76d6ea106c93adb4e56c41e0da79658e56e547d2c" 455 | ], 456 | "index": "pypi", 457 | "version": "==1.12.1" 458 | }, 459 | "urllib3": { 460 | "hashes": [ 461 | "sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0", 462 | "sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3" 463 | ], 464 | "index": "pypi", 465 | "version": "==1.24.2" 466 | }, 467 | "webencodings": { 468 | "hashes": [ 469 | "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", 470 | "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" 471 | ], 472 | "version": "==0.5.1" 473 | }, 474 | "zipp": { 475 | "hashes": [ 476 | "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", 477 | "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" 478 | ], 479 | "version": "==0.6.0" 480 | } 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # LogBot [![](https://img.shields.io/pypi/v/logbot-telegram.svg?style=flat-square)](https://pypi.org/project/logbot-telegram/) [![](https://img.shields.io/pypi/pyversions/logbot-telegram.svg?style=flat-square)](https://pypi.org/project/logbot-telegram/) [![](https://img.shields.io/badge/chat%20on-telegram-red.svg?style=flat-square&logo=telegram)](https://t.me/apiad_demo_logbot) 2 | 3 | > A Telegram bot that you can log to from bash or Python and manage long running processes. 4 | 5 | ``` 6 | pip install logbot-telegram 7 | ``` 8 | 9 | ## Quickstart 10 | 11 | > **NOTE:** The following example is using a _demo_ bot that I personally host at [apiad.net:6778](http://apiad.net:6778). This bot can and will be stopped, restarted or even destroyed without notice. Refer to the section on **Setting your own** for more details. 12 | 13 | First, go to Telegram and open a chat with [LogBot (Demo)](http://t.me/apiad_demo_logbot). You'll see this initial message. 14 | 15 | ![](img/img1.png) 16 | 17 | Hit the **Start** button, and the bot will send you a *secret token*: 18 | 19 | ![](img/img2.png) 20 | 21 | Now with this secret token you can communicate to the bot through HTTP. The protocol is very simple. Just a send a `POST` request to the bot's API with an encoded `json` data: 22 | 23 | ```bash 24 | curl -XPOST http://apiad.net:6778 -d \ 25 | "{ \ 26 | \"token\": \"8PmUheSK6Zow\", \ 27 | \"msg\": \"Hello World\" \ 28 | }" 29 | {"msg":"Hello World", "id":121} 30 | ``` 31 | 32 | Voilá, your message will be forwared to Telegram: 33 | 34 | ![](img/img3.png) 35 | 36 | You can send messages with Markdown formatting: 37 | 38 | ```bash 39 | curl -XPOST http://apiad.net:6778 -d \ 40 | "{ \ 41 | \"token\": \"8PmUheSK6Zow\", \ 42 | \"msg\": \"*Hello* _World_\" \ 43 | }" 44 | {"msg":"*Hello* _World_", "id":122} 45 | ``` 46 | 47 | ![](img/img4.png) 48 | 49 | Finally, you can send **actions**, that will be rendered as buttons in Telegram. 50 | When you click the button, and **only when you click it**, you will get a response back with the clicked option: 51 | 52 | ```bash 53 | curl -XPOST http://apiad.net:6778 -d \ 54 | "{ \ 55 | \"token\": \"8PmUheSK6Zow\", \ 56 | \"msg\": \"Wanna try?\", \ 57 | \"actions\": [\"Yes\", \"No way\"] \ 58 | }" 59 | {"msg":"Wanna try?","response":"Yes", "id":123} 60 | ``` 61 | 62 | ![](img/img5.png) 63 | 64 | > **NOTE:** By default you have 60 seconds to answer before a response timeout is raised. This hard limit might be changed in future versions. 65 | 66 | If you send a `progress` key with a value between `0` and `1` (`float`), the message will also render a progress bar. 67 | If you send an `edit` key with the value (`int`) of the `id` of a previous response, you will edit that message instead of sending a new one. 68 | You can use these two features together to create animated progress bars. 69 | 70 | ```bash 71 | curl -XPOST http://apiad.net:6778 -d \ 72 | "{ \ 73 | \"token\": \"8PmUheSK6Zow\", \ 74 | \"msg\": \"It works\", \ 75 | \"progress\": 0.3 \ 76 | }" 77 | {"msg":"It works", "id":124} 78 | ``` 79 | 80 | ![](img/img6.png) 81 | 82 | ## Python API 83 | 84 | If you talk Python, you can clone this project and use a set of simple tools to skip all that `curl`. 85 | 86 | ``` 87 | pip install logbot-telegram 88 | ``` 89 | 90 | * `send` simply sends the message. 91 | 92 | ```python 93 | >>> from logbot import Client 94 | >>> c = Client(token="", host="http://apiad.net", port=6778) 95 | >>> c.send("Hello World") 96 | ``` 97 | 98 | * `ask` will send the corresponding questions as buttons and return the reply: 99 | 100 | ```python 101 | >>> c.ask("Do you?", "A", "B", "C") 102 | 'C' # Supposedly you hit C in Telegram 103 | ``` 104 | 105 | * `yes` will simply send `Yes` and `No` and return `True` or `False`: 106 | 107 | ```python 108 | >>> if c.yes("Do you?"): 109 | >>> print("It does!") 110 | 'It does!' # Supposedly you hit Yes in Telegram 111 | ``` 112 | 113 | The `send` method also receives an optional `progress` value to render a progress bar. 114 | Also, a parameter `edit=True` will make it edit the last send message, instead of a new one. 115 | With these options you can make an animated progress bar like: 116 | 117 | ```python 118 | >>> for i in range(0, 11): 119 | ... c.send("Progress", progress=i/10, edit=True) 120 | ... time.sleep(1) 121 | ``` 122 | 123 | These are also available as simple functions, which receive `token`, `host` and `port` on every call. The `Client` class just simplifies passing those values all the time. 124 | 125 | ## What can I do with this? 126 | 127 | Some simple ideas: 128 | 129 | * **Log directly into Telegram from your code**. For example, using Python `request` you can easily make any long-running script to periodically send updates. 130 | * **Monitor a server**. Using `bash`, `curl` and `cron` you can send yourself periodically info about your CPU, memory and I/O ussage. 131 | * **Quick responses on Telegram**. By using the response actions, you can interact with running code, either to take decisions, kill long running processes, retry things that broke, etc. Your imagination is the limit. 132 | 133 | ## Setting up your own 134 | 135 | For testing, and while my server handles it, you can use the bot I have set up. It's free and for that reason I might decide to turn it off at any given time without any advice. So, if you want a long time working solution, you will have to set up your own. 136 | 137 | 1. Create your Telegram bot with [BotFather](t.me/botfather). If you don't know what I mean, read the Telegram documentation on bots. It's all over the Internet. 138 | 2. BotFather will give you a `TOKEN`. Make sure to write it down. 139 | 3. Run `pip install logbot-telegram`. 140 | 4. Run `export TOKEN=`. 141 | 5. You can run `python -m logbot` now. It will listen by default at `localhost:6778`. 142 | 6. You can change the `HOST` and `PORT` with environment variables. 143 | 144 | ## Collaboration 145 | 146 | It's MIT, so you know the drill. Fork, edit, pull request, repeat. 147 | 148 | ## License 149 | 150 | > MIT License 151 | > 152 | > Copyright (c) 2019 Alejandro Piad 153 | > 154 | > Permission is hereby granted, free of charge, to any person obtaining a copy 155 | > of this software and associated documentation files (the "Software"), to deal 156 | > in the Software without restriction, including without limitation the rights 157 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 158 | > copies of the Software, and to permit persons to whom the Software is 159 | > furnished to do so, subject to the following conditions: 160 | > 161 | > The above copyright notice and this permission notice shall be included in all 162 | > copies or substantial portions of the Software. 163 | > 164 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 165 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 166 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 167 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 168 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 169 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 170 | > SOFTWARE. 171 | -------------------------------------------------------------------------------- /img/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiad/logbot/f9292286a1984310cc3c31b9982e161990f51b7a/img/img1.png -------------------------------------------------------------------------------- /img/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiad/logbot/f9292286a1984310cc3c31b9982e161990f51b7a/img/img2.png -------------------------------------------------------------------------------- /img/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiad/logbot/f9292286a1984310cc3c31b9982e161990f51b7a/img/img3.png -------------------------------------------------------------------------------- /img/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiad/logbot/f9292286a1984310cc3c31b9982e161990f51b7a/img/img4.png -------------------------------------------------------------------------------- /img/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiad/logbot/f9292286a1984310cc3c31b9982e161990f51b7a/img/img5.png -------------------------------------------------------------------------------- /img/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiad/logbot/f9292286a1984310cc3c31b9982e161990f51b7a/img/img6.png -------------------------------------------------------------------------------- /logbot/__init__.py: -------------------------------------------------------------------------------- 1 | from .bot import * 2 | from .utils import * 3 | -------------------------------------------------------------------------------- /logbot/__main__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .bot import Bot 4 | 5 | 6 | if __name__ == "__main__": 7 | bot = Bot(os.getenv("TOKEN")) 8 | bot.run(host=os.getenv("HOST"), port=int(os.getenv("PORT"))) 9 | -------------------------------------------------------------------------------- /logbot/bot.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import random 4 | import asyncio 5 | 6 | from emoji import emojize 7 | from telegram.ext import Updater, CommandHandler, CallbackQueryHandler 8 | from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton 9 | from threading import Thread 10 | from queue import Queue 11 | from sanic import Sanic 12 | from sanic.response import json 13 | 14 | 15 | def random_key(size): 16 | symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 17 | symbols = symbols + symbols.lower() 18 | symbols = symbols + "0123456789" 19 | 20 | return "".join(random.sample(symbols, size)) 21 | 22 | 23 | BLACK = emojize(":black_medium_square:") 24 | WHITE = emojize(":white_medium_square:") 25 | 26 | 27 | class Bot: 28 | def __init__(self, token): 29 | self.token = token 30 | self.chat_ids = dict() 31 | self.updater = Updater(self.token) 32 | self.dispatcher = self.updater.dispatcher 33 | 34 | # register bot methods 35 | self.bot = self.updater.bot 36 | self.dispatcher.add_handler(CommandHandler('start', self._start)) 37 | self.dispatcher.add_handler(CallbackQueryHandler(self._callback)) 38 | 39 | # register bot endpoints 40 | self.app = Sanic("message-queue") 41 | self.app.add_route(self._post, "/", methods=['POST']) 42 | self.app.register_listener(self.stop, "before_server_stop") 43 | 44 | # callback ids 45 | self.callbacks = {} 46 | 47 | async def _post(self, request): 48 | data = request.json 49 | 50 | token = data['token'] 51 | msg = data['msg'] 52 | user = self.chat_ids[token] 53 | actions = data.get('actions', []) 54 | reply_token = random_key(12) 55 | progress = data.get('progress', None) 56 | edit = data.get('edit', None) 57 | 58 | text = msg 59 | 60 | if isinstance(progress, float) and 0 <= progress <= 1: 61 | blacks = int(progress * 10) 62 | whites = 10 - blacks 63 | text += "\n" 64 | 65 | for i in range(blacks): 66 | text += BLACK 67 | for i in range(whites): 68 | text += WHITE 69 | 70 | if actions: 71 | reply_markup = InlineKeyboardMarkup([[ 72 | InlineKeyboardButton(s, callback_data=reply_token + "/" + s) for s in actions 73 | ]]) 74 | else: 75 | reply_markup = None 76 | 77 | if isinstance(edit, int): 78 | message = self.bot.edit_message_text(text=text, chat_id=user, message_id=edit, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup) 79 | else: 80 | message = self.bot.send_message(text=text, chat_id=user, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup) 81 | 82 | 83 | if not actions: 84 | return json(dict(msg=msg, id=message.message_id)) 85 | 86 | while not reply_token in self.callbacks: 87 | await asyncio.sleep(1) 88 | 89 | response = self.callbacks.pop(reply_token) 90 | return json(dict(msg=msg, response=response, id=message.message_id)) 91 | 92 | def _callback(self, bot, update): 93 | token, response = update.callback_query.data.split("/") 94 | update.callback_query.answer() 95 | update.callback_query.edit_message_reply_markup(reply_markup=None) 96 | self.callbacks[token] = response 97 | 98 | def _start(self, bot, update): 99 | msg_token = random_key(12) 100 | self.chat_ids[msg_token] = update.message.chat_id 101 | update.message.reply_markdown("This is your custom message token:\n*{0}*".format(msg_token)) 102 | 103 | def run(self, host='localhost', port=6778): 104 | self.updater.start_polling() 105 | self.app.run(host, port) 106 | 107 | def stop(self, app, loop): 108 | self.updater.stop() 109 | -------------------------------------------------------------------------------- /logbot/utils.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | def send(token, msg, edit=None, progress=None, host="http://localhost", port=6778): 5 | response = requests.post("%s:%d" % (host,port), json=dict( 6 | token=token, 7 | msg=msg, 8 | progress=progress, 9 | edit=edit 10 | )) 11 | 12 | return response.json() 13 | 14 | def ask(token, msg, *actions, host="http://localhost", port=6778): 15 | response = requests.post("%s:%d" % (host,port), json=dict( 16 | token=token, 17 | msg=msg, 18 | actions=list(actions) 19 | )) 20 | 21 | try: 22 | return response.json()['response'] 23 | except: 24 | return None 25 | 26 | def yes(token, msg, host="http://localhost", port=6778): 27 | return ask(token, msg, 'Yes', 'No', host=host, port=port) == 'Yes' 28 | 29 | 30 | class Client: 31 | def __init__(self, token, host="http://localhost", port=6778): 32 | self.token = token 33 | self.host = host 34 | self.port = port 35 | self.last_id = None 36 | 37 | def send(self, msg, progress=None, edit=False): 38 | self.last_id = send(self.token, msg, progress=progress, edit=self.last_id if edit else None, host=self.host, port=self.port)['id'] 39 | 40 | def ask(self, msg, *actions): 41 | return ask(self.token, msg, *actions, host=self.host, port=self.port) 42 | 43 | def yes(self, msg): 44 | return yes(self.token, msg, self.host, self.port) 45 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | build: 2 | python setup.py sdist bdist_wheel 3 | 4 | upload-pypi: 5 | python -m twine upload dist/* 6 | 7 | upload-test: 8 | python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* 9 | 10 | clean: 11 | rm -rf dist/* 12 | rm -rf build/* 13 | 14 | test: 15 | python -m pytest 16 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | long_description = """ 4 | # LogBot [![](https://img.shields.io/pypi/v/logbot-telegram.svg?style=flat-square)](https://pypi.org/project/logbot-telegram/) [![](https://img.shields.io/pypi/pyversions/logbot-telegram.svg?style=flat-square)](https://pypi.org/project/logbot-telegram/) 5 | 6 | A Telegram bot that you can log to from Python and manage long running processes. 7 | Check the [Readme](https://github.com/apiad/logbot) for use and installation instructions. 8 | """ 9 | 10 | setuptools.setup( 11 | name="logbot-telegram", 12 | version="0.1.5", 13 | author="Alejandro Piad", 14 | author_email="apiad@apiad.net", 15 | description="A Telegram bot that you can log to from Python and manage long running processes.", 16 | long_description=long_description, 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/apiad/logbot", 19 | packages=['logbot'], 20 | install_requires=[ 21 | 'python-telegram-bot==11.1.0', 22 | 'sanic==18.12.0', 23 | 'emoji==0.5.1', 24 | 'requests==2.21.0' 25 | ], 26 | classifiers=[ 27 | "Programming Language :: Python :: 3.6", 28 | "License :: OSI Approved :: MIT License", 29 | "Operating System :: OS Independent", 30 | ], 31 | ) 32 | --------------------------------------------------------------------------------