chgksuite 0.27.1__tar.gz → 0.28.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chgksuite-0.27.1 → chgksuite-0.28.0}/PKG-INFO +1 -1
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/cli.py +14 -1
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/common.py +14 -2
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/__init__.py +2 -3
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/telegram.py +130 -84
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/parser.py +7 -3
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/trello.py +2 -3
- chgksuite-0.28.0/chgksuite/version.py +1 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/pyproject.toml +7 -0
- chgksuite-0.27.1/.github/workflows/build.yml +0 -283
- chgksuite-0.27.1/.gitlab-ci.yml +0 -17
- chgksuite-0.27.1/.~lock.results.xlsx# +0 -1
- chgksuite-0.27.1/MANIFEST.in +0 -7
- chgksuite-0.27.1/adhoc/regexes_fixer.py +0 -66
- chgksuite-0.27.1/author_counter.py +0 -24
- chgksuite-0.27.1/chgksuite/version.py +0 -1
- chgksuite-0.27.1/debug_0.txt +0 -3
- chgksuite-0.27.1/debug_1.json +0 -10
- chgksuite-0.27.1/debug_1a.json +0 -10
- chgksuite-0.27.1/debug_2.json +0 -10
- chgksuite-0.27.1/debug_3.json +0 -10
- chgksuite-0.27.1/debug_4.json +0 -14
- chgksuite-0.27.1/debug_5.json +0 -14
- chgksuite-0.27.1/debug_6.json +0 -10
- chgksuite-0.27.1/debug_final.json +0 -10
- chgksuite-0.27.1/dev_readme.md +0 -25
- chgksuite-0.27.1/docs/.cache/.gitignore +0 -1
- chgksuite-0.27.1/docs/.cache/12897346880794287463 +0 -43
- chgksuite-0.27.1/docs/.cache/13071136909783630831 +0 -27
- chgksuite-0.27.1/docs/.cache/13307108790744403429 +0 -58
- chgksuite-0.27.1/docs/.cache/13333983167045209281 +0 -27
- chgksuite-0.27.1/docs/.cache/14085495286103177657 +0 -4
- chgksuite-0.27.1/docs/.cache/15213489598760314030 +0 -149
- chgksuite-0.27.1/docs/.cache/15319492834190874243 +0 -4
- chgksuite-0.27.1/docs/.cache/17387058097034297385 +0 -4
- chgksuite-0.27.1/docs/.cache/17722115158263369208 +0 -4
- chgksuite-0.27.1/docs/.cache/18247088324849079572 +0 -4
- chgksuite-0.27.1/docs/.cache/1840587404175770520 +0 -43
- chgksuite-0.27.1/docs/.cache/2517734129797249711 +0 -4
- chgksuite-0.27.1/docs/.cache/273431981325847810 +0 -164
- chgksuite-0.27.1/docs/.cache/2872065536916780406 +0 -27
- chgksuite-0.27.1/docs/.cache/3476900567878811119 +0 -4
- chgksuite-0.27.1/docs/.cache/4757258273854424423 +0 -58
- chgksuite-0.27.1/docs/.cache/5728722062471071239 +0 -58
- chgksuite-0.27.1/docs/.cache/6061198691539556612 +0 -4
- chgksuite-0.27.1/docs/.cache/6079179394456200124 +0 -4
- chgksuite-0.27.1/docs/.cache/735877668610100612 +0 -73
- chgksuite-0.27.1/docs/.cache/9345880734637750342 +0 -4
- chgksuite-0.27.1/docs/.cache/9490951945832949972 +0 -4
- chgksuite-0.27.1/docs/docs/4s.md +0 -229
- chgksuite-0.27.1/docs/docs/add_stats.md +0 -9
- chgksuite-0.27.1/docs/docs/base.md +0 -43
- chgksuite-0.27.1/docs/docs/i14n.md +0 -31
- chgksuite-0.27.1/docs/docs/images/base.png +0 -0
- chgksuite-0.27.1/docs/docs/images/douplet_problem.png +0 -0
- chgksuite-0.27.1/docs/docs/images/i14n.png +0 -0
- chgksuite-0.27.1/docs/docs/images/i14n_parse.png +0 -0
- chgksuite-0.27.1/docs/docs/images/lj.png +0 -0
- chgksuite-0.27.1/docs/docs/images/main.png +0 -0
- chgksuite-0.27.1/docs/docs/images/openquiz.png +0 -0
- chgksuite-0.27.1/docs/docs/images/openquiz2.png +0 -0
- chgksuite-0.27.1/docs/docs/images/parse.png +0 -0
- chgksuite-0.27.1/docs/docs/images/pptx.png +0 -0
- chgksuite-0.27.1/docs/docs/images/pptx_additional_conf.png +0 -0
- chgksuite-0.27.1/docs/docs/images/pptx_slide_a.png +0 -0
- chgksuite-0.27.1/docs/docs/images/pptx_slide_q.png +0 -0
- chgksuite-0.27.1/docs/docs/images/roenko.png +0 -0
- chgksuite-0.27.1/docs/docs/images/rozhdsushkov.png +0 -0
- chgksuite-0.27.1/docs/docs/images/stats.png +0 -0
- chgksuite-0.27.1/docs/docs/images/telegram.png +0 -0
- chgksuite-0.27.1/docs/docs/images/trello_download.png +0 -0
- chgksuite-0.27.1/docs/docs/images/trello_token.png +0 -0
- chgksuite-0.27.1/docs/docs/images/trello_upload.png +0 -0
- chgksuite-0.27.1/docs/docs/images/word.png +0 -0
- chgksuite-0.27.1/docs/docs/index.md +0 -59
- chgksuite-0.27.1/docs/docs/lj.md +0 -9
- chgksuite-0.27.1/docs/docs/openquiz.md +0 -7
- chgksuite-0.27.1/docs/docs/pptx.md +0 -66
- chgksuite-0.27.1/docs/docs/stylesheets/extra.css +0 -29
- chgksuite-0.27.1/docs/docs/telegram.md +0 -46
- chgksuite-0.27.1/docs/docs/trello.md +0 -31
- chgksuite-0.27.1/docs/docs/word.md +0 -24
- chgksuite-0.27.1/docs/mkdocs.yml +0 -35
- chgksuite-0.27.1/history.md +0 -404
- chgksuite-0.27.1/hook-dateparser.py +0 -12
- chgksuite-0.27.1/packer.py +0 -172
- chgksuite-0.27.1/pytest.ini +0 -5
- chgksuite-0.27.1/pytest.sh +0 -7
- chgksuite-0.27.1/re_helper.py +0 -24
- chgksuite-0.27.1/requirements_dev.txt +0 -2
- chgksuite-0.27.1/results.xlsx +0 -0
- chgksuite-0.27.1/ruff.toml +0 -2
- chgksuite-0.27.1/test_multiple_images.txt +0 -3
- chgksuite-0.27.1/test_oschr/404.jpg +0 -0
- chgksuite-0.27.1/test_oschr/Arhipova_Shtirlic-shel-po-koridoru-.803300.pdf/stirlitz_was_walking.fbd +0 -525
- chgksuite-0.27.1/test_oschr/Arhipova_Shtirlic-shel-po-koridoru-.803300.pdf/stirlitz_was_walking.pdf +0 -0
- chgksuite-0.27.1/test_oschr/alexeeva.jpeg +0 -0
- chgksuite-0.27.1/test_oschr/arhipova.txt +0 -1030
- chgksuite-0.27.1/test_oschr/ber.png +0 -0
- chgksuite-0.27.1/test_oschr/columns.txt +0 -35
- chgksuite-0.27.1/test_oschr/columns_ru.pdf +0 -0
- chgksuite-0.27.1/test_oschr/columns_ru.tex +0 -625
- chgksuite-0.27.1/test_oschr/cool.pdf +0 -0
- chgksuite-0.27.1/test_oschr/cool.png +0 -0
- chgksuite-0.27.1/test_oschr/dish.png +0 -0
- chgksuite-0.27.1/test_oschr/dubinki.png +0 -0
- chgksuite-0.27.1/test_oschr/duran01.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran02.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran03.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran04.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran05.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran06.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran07.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran08.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran09.jpg +0 -0
- chgksuite-0.27.1/test_oschr/duran10.jpg +0 -0
- chgksuite-0.27.1/test_oschr/eisenhower.png +0 -0
- chgksuite-0.27.1/test_oschr/emo.pdf +0 -0
- chgksuite-0.27.1/test_oschr/emo.png +0 -0
- chgksuite-0.27.1/test_oschr/emosong.png +0 -0
- chgksuite-0.27.1/test_oschr/emosong1.png +0 -0
- chgksuite-0.27.1/test_oschr/emosong2.png +0 -0
- chgksuite-0.27.1/test_oschr/handouts_test.txt +0 -30
- chgksuite-0.27.1/test_oschr/handouts_test_ru.tex +0 -296
- chgksuite-0.27.1/test_oschr/karikatura_razdatka.png +0 -0
- chgksuite-0.27.1/test_oschr/kuzmin-tat.png +0 -0
- chgksuite-0.27.1/test_oschr/kuzmin.txt +0 -13242
- chgksuite-0.27.1/test_oschr/otbor_studchr.txt +0 -79
- chgksuite-0.27.1/test_oschr/otbor_studchr_handouts.txt +0 -16
- chgksuite-0.27.1/test_oschr/otbor_studchr_handouts_ru.pdf +0 -0
- chgksuite-0.27.1/test_oschr/panchenko.txt +0 -23670
- chgksuite-0.27.1/test_oschr/testgrid.txt +0 -15
- chgksuite-0.27.1/test_oschr/testgrid_ru.pdf +0 -0
- chgksuite-0.27.1/test_oschr/testgrid_ru.tex +0 -325
- chgksuite-0.27.1/test_oschr/zine.png +0 -0
- chgksuite-0.27.1/test_with_image.txt +0 -3
- chgksuite-0.27.1/test_with_real_image.txt +0 -3
- chgksuite-0.27.1/tests/2019-07-23_beln19_u.docx +0 -0
- chgksuite-0.27.1/tests/2019-07-23_beln19_u.docx.canon +0 -690
- chgksuite-0.27.1/tests/Kubok_knyagini_Olgi-2015.docx +0 -0
- chgksuite-0.27.1/tests/Kubok_knyagini_Olgi-2015.docx.canon +0 -644
- chgksuite-0.27.1/tests/Shkolny_Chemp_Estonii-2014_(48v).docx +0 -0
- chgksuite-0.27.1/tests/Shkolny_Chemp_Estonii-2014_(48v).docx.canon +0 -361
- chgksuite-0.27.1/tests/__init__.py +0 -0
- chgksuite-0.27.1/tests/balt09-1.txt +0 -783
- chgksuite-0.27.1/tests/balt09-1.txt.canon +0 -286
- chgksuite-0.27.1/tests/borromeo.txt +0 -11
- chgksuite-0.27.1/tests/borromeo.txt.canon +0 -16
- chgksuite-0.27.1/tests/canonize.py +0 -69
- chgksuite-0.27.1/tests/chgksuite_test.py +0 -364
- chgksuite-0.27.1/tests/conftest.py +0 -21
- chgksuite-0.27.1/tests/encrypt_test_files.py +0 -147
- chgksuite-0.27.1/tests/haifa2025.docx +0 -0
- chgksuite-0.27.1/tests/haifa2025.docx.canon +0 -349
- chgksuite-0.27.1/tests/haifa2025.docx.encrypted +0 -0
- chgksuite-0.27.1/tests/haifa2025.docx.encrypted.canon +0 -0
- chgksuite-0.27.1/tests/handouter_layout_test.py +0 -310
- chgksuite-0.27.1/tests/link_unwrap.docx +0 -0
- chgksuite-0.27.1/tests/link_unwrap.docx.canon +0 -10
- chgksuite-0.27.1/tests/ljcredentials +0 -1
- chgksuite-0.27.1/tests/long_handout.png +0 -0
- chgksuite-0.27.1/tests/malkin_papkov_synchr.docx +0 -0
- chgksuite-0.27.1/tests/malkin_papkov_synchr.docx.canon +0 -99
- chgksuite-0.27.1/tests/octo2021_khmelkov.docx +0 -0
- chgksuite-0.27.1/tests/octo2021_khmelkov.docx.canon +0 -102
- chgksuite-0.27.1/tests/ovsch_boronenko_3.4s +0 -334
- chgksuite-0.27.1/tests/ovsch_boronenko_3.docx +0 -0
- chgksuite-0.27.1/tests/ovsch_boronenko_3.docx.canon +0 -334
- chgksuite-0.27.1/tests/pass1.docx +0 -0
- chgksuite-0.27.1/tests/pass1.docx.canon +0 -183
- chgksuite-0.27.1/tests/settings.json +0 -3
- chgksuite-0.27.1/tests/single_number_line_test.docx +0 -0
- chgksuite-0.27.1/tests/single_number_line_test.docx.canon +0 -46
- chgksuite-0.27.1/tests/smalltest.4s +0 -6
- chgksuite-0.27.1/tests/test.jpg +0 -0
- chgksuite-0.27.1/tests/test_blitz.txt +0 -8
- chgksuite-0.27.1/tests/test_blitz.txt.canon +0 -9
- chgksuite-0.27.1/tests/tourrev_with_razmin.docx +0 -0
- chgksuite-0.27.1/tests/tourrev_with_razmin.docx.canon +0 -34
- chgksuite-0.27.1/tests//320/241/320/247/320/240_/320/250/320/265/321/200/320/265/320/264/320/265/320/263/320/260_/320/225/321/200/320/274/320/270/321/210/320/272/320/270/320/275.docx +0 -0
- chgksuite-0.27.1/tests//320/241/320/247/320/240_/320/250/320/265/321/200/320/265/320/264/320/265/320/263/320/260_/320/225/321/200/320/274/320/270/321/210/320/272/320/270/320/275.docx.canon +0 -13
- chgksuite-0.27.1/uv.lock +0 -1454
- chgksuite-0.27.1//320/235/320/276/321/207/321/214/320/241/320/276/320/263/321/200/320/265/320/262/320/260/321/216/321/211/320/265/320/263/320/276/320/236/321/207/320/260/320/263/320/2602025_001.jpeg +0 -0
- chgksuite-0.27.1//320/235/320/276/321/207/321/214/320/241/320/276/320/263/321/200/320/265/320/262/320/260/321/216/321/211/320/265/320/263/320/276/320/236/321/207/320/260/320/263/320/2602025_with_stats1.4s +0 -373
- chgksuite-0.27.1//320/235/320/276/321/207/321/214/320/241/320/276/320/263/321/200/320/265/320/262/320/260/321/216/321/211/320/265/320/263/320/276/320/236/321/207/320/260/320/263/320/2602025_with_stats1.dbg +0 -562
- chgksuite-0.27.1//320/235/320/276/321/207/321/214/320/241/320/276/320/263/321/200/320/265/320/262/320/260/321/216/321/211/320/265/320/263/320/276/320/236/321/207/320/260/320/263/320/2602025_with_stats1.docx +0 -0
- chgksuite-0.27.1//321/216 +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/.gitignore +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/.hgignore +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/LICENSE +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/README.md +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/__init__.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/__main__.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/_html2md.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/chgksuite_parser.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/composer_common.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/db.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/docx.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/latex.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/lj.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/markdown.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/openquiz.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/pptx.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/stats.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/composer/telegram_bot.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/__init__.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/gen.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/installer.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/pack.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/runner.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/tex_internals.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/handouter/utils.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/lastdir +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/parser_db.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/cheader.tex +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/fix-unnumbered-sections.sty +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_az.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_by.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_by_tar.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_en.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_kz_cyr.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_ru.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_sr.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_ua.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_uz.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/labels_uz_cyr.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/pptx_config.toml +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_az.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_by.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_by_tar.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_en.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_kz_cyr.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_ru.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_sr.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_ua.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_uz.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/regexes_uz_cyr.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/template.docx +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/template.pptx +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/resources/trello.json +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/typotools.py +0 -0
- {chgksuite-0.27.1 → chgksuite-0.28.0}/chgksuite/vulture_whitelist.py +0 -0
|
@@ -12,6 +12,7 @@ from chgksuite.common import (
|
|
|
12
12
|
load_settings,
|
|
13
13
|
)
|
|
14
14
|
from chgksuite.composer import gui_compose
|
|
15
|
+
from chgksuite.composer.telegram import get_saved_telegram_targets
|
|
15
16
|
from chgksuite.handouter.runner import gui_handouter
|
|
16
17
|
from chgksuite.parser import gui_parse
|
|
17
18
|
from chgksuite.trello import gui_trello
|
|
@@ -34,7 +35,14 @@ class ArgparseBuilder:
|
|
|
34
35
|
if self.use_wrapper:
|
|
35
36
|
return getattr(parser, func)(*args, **kwargs)
|
|
36
37
|
else:
|
|
37
|
-
for k in (
|
|
38
|
+
for k in (
|
|
39
|
+
"caption",
|
|
40
|
+
"advanced",
|
|
41
|
+
"argtype",
|
|
42
|
+
"hide",
|
|
43
|
+
"filetypes",
|
|
44
|
+
"combobox_values",
|
|
45
|
+
):
|
|
38
46
|
try:
|
|
39
47
|
kwargs.pop(k)
|
|
40
48
|
except KeyError:
|
|
@@ -654,12 +662,15 @@ class ArgparseBuilder:
|
|
|
654
662
|
help="a made-up string designating account to use.",
|
|
655
663
|
caption="Аккаунт для постинга",
|
|
656
664
|
)
|
|
665
|
+
saved_targets = get_saved_telegram_targets()
|
|
657
666
|
self.add_argument(
|
|
658
667
|
cmdcompose_telegram,
|
|
659
668
|
"--tgchannel",
|
|
660
669
|
required=True,
|
|
661
670
|
help="a channel to post questions to.",
|
|
662
671
|
caption="Название канала, в который постим",
|
|
672
|
+
argtype="combobox",
|
|
673
|
+
combobox_values=saved_targets,
|
|
663
674
|
)
|
|
664
675
|
self.add_argument(
|
|
665
676
|
cmdcompose_telegram,
|
|
@@ -667,6 +678,8 @@ class ArgparseBuilder:
|
|
|
667
678
|
required=True,
|
|
668
679
|
help="a chat connected to the channel.",
|
|
669
680
|
caption="Название чата, привязанного к каналу",
|
|
681
|
+
argtype="combobox",
|
|
682
|
+
combobox_values=saved_targets,
|
|
670
683
|
)
|
|
671
684
|
self.add_argument(
|
|
672
685
|
cmdcompose_telegram,
|
|
@@ -27,7 +27,7 @@ QUESTION_LABELS = [
|
|
|
27
27
|
"number",
|
|
28
28
|
"setcounter",
|
|
29
29
|
]
|
|
30
|
-
SEP =
|
|
30
|
+
SEP = "\n"
|
|
31
31
|
try:
|
|
32
32
|
ENC = sys.stdout.encoding or "utf8"
|
|
33
33
|
except AttributeError:
|
|
@@ -152,6 +152,19 @@ def ensure_utf8(s):
|
|
|
152
152
|
return s
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
def read_text_file(filepath, encoding="utf-8"):
|
|
156
|
+
"""Read a text file, fixing corrupted line endings (\r\r\n -> \n) if present."""
|
|
157
|
+
with open(filepath, "rb") as f:
|
|
158
|
+
raw = f.read()
|
|
159
|
+
# Fix corrupted line endings at byte level before decoding
|
|
160
|
+
if b"\r\r\n" in raw:
|
|
161
|
+
raw = raw.replace(b"\r\r\n", b"\n")
|
|
162
|
+
text = raw.decode(encoding)
|
|
163
|
+
# Normalize any remaining line endings
|
|
164
|
+
text = text.replace("\r\n", "\n").replace("\r", "\n")
|
|
165
|
+
return text
|
|
166
|
+
|
|
167
|
+
|
|
155
168
|
class DummyLogger(object):
|
|
156
169
|
def info(self, *args, **kwargs):
|
|
157
170
|
pass
|
|
@@ -370,6 +383,5 @@ def compose_4s(structure, args=None):
|
|
|
370
383
|
+ SEP
|
|
371
384
|
)
|
|
372
385
|
tmp = re.sub(r"{}+".format(SEP), SEP, tmp)
|
|
373
|
-
tmp = tmp.replace("\r\r", "\r")
|
|
374
386
|
result += tmp + SEP
|
|
375
387
|
return result
|
|
@@ -12,6 +12,7 @@ from chgksuite.common import (
|
|
|
12
12
|
get_source_dirs,
|
|
13
13
|
init_logger,
|
|
14
14
|
log_wrap,
|
|
15
|
+
read_text_file,
|
|
15
16
|
set_lastdir,
|
|
16
17
|
)
|
|
17
18
|
from chgksuite.composer.chgksuite_parser import parse_4s
|
|
@@ -74,9 +75,7 @@ def process_file_wrapper(filename, sourcedir, targetdir, args):
|
|
|
74
75
|
|
|
75
76
|
def parse_filepath(filepath, args=None):
|
|
76
77
|
args = args or DefaultArgs()
|
|
77
|
-
|
|
78
|
-
input_text = input_file.read()
|
|
79
|
-
input_text = input_text.replace("\r", "")
|
|
78
|
+
input_text = read_text_file(filepath)
|
|
80
79
|
debug_dir = os.path.dirname(os.path.abspath(filepath))
|
|
81
80
|
return parse_4s(
|
|
82
81
|
input_text, randomize=args.randomize, debug=args.debug, debug_dir=debug_dir
|
|
@@ -17,6 +17,28 @@ from chgksuite.composer.composer_common import BaseExporter, parseimg
|
|
|
17
17
|
from chgksuite.composer.telegram_bot import run_bot_in_thread
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def get_saved_telegram_targets():
|
|
21
|
+
"""
|
|
22
|
+
Load all saved channel/chat usernames from resolve.db.
|
|
23
|
+
Returns a list of usernames that have been previously used.
|
|
24
|
+
"""
|
|
25
|
+
chgksuite_dir = get_chgksuite_dir()
|
|
26
|
+
resolve_db_path = os.path.join(chgksuite_dir, "resolve.db")
|
|
27
|
+
|
|
28
|
+
if not os.path.exists(resolve_db_path):
|
|
29
|
+
return []
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
conn = sqlite3.connect(resolve_db_path)
|
|
33
|
+
cursor = conn.cursor()
|
|
34
|
+
cursor.execute("SELECT username FROM resolve ORDER BY username")
|
|
35
|
+
results = cursor.fetchall()
|
|
36
|
+
conn.close()
|
|
37
|
+
return [row[0] for row in results]
|
|
38
|
+
except Exception:
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
|
|
20
42
|
def get_text(msg_data):
|
|
21
43
|
if "message" in msg_data and "text" in msg_data["message"]:
|
|
22
44
|
return msg_data["message"]["text"]
|
|
@@ -44,19 +66,14 @@ class TelegramExporter(BaseExporter):
|
|
|
44
66
|
self.chat_id = None # Discussion group ID linked to the channel
|
|
45
67
|
self.auth_uuid = uuid.uuid4().hex[:8]
|
|
46
68
|
self.chat_auth_uuid = uuid.uuid4().hex[:8]
|
|
69
|
+
self.session = requests.Session()
|
|
47
70
|
self.init_telegram()
|
|
48
71
|
|
|
49
72
|
def check_connectivity(self):
|
|
50
|
-
|
|
51
|
-
if req_me.status_code != 200:
|
|
52
|
-
raise Exception(
|
|
53
|
-
f"getMe request wasn't successful: {req_me.status_code} {req_me.text}"
|
|
54
|
-
)
|
|
55
|
-
obj = req_me.json()
|
|
56
|
-
assert obj["ok"]
|
|
73
|
+
result = self.send_api_request("getMe")
|
|
57
74
|
if self.args.debug:
|
|
58
|
-
print(f"connection successful! {
|
|
59
|
-
self.bot_id =
|
|
75
|
+
print(f"connection successful! {result}")
|
|
76
|
+
self.bot_id = result["id"]
|
|
60
77
|
|
|
61
78
|
def init_temp_db(self):
|
|
62
79
|
self.db_conn = sqlite3.connect(self.temp_db_path)
|
|
@@ -102,8 +119,6 @@ class TelegramExporter(BaseExporter):
|
|
|
102
119
|
).fetchall()
|
|
103
120
|
if messages and json.loads(messages[0][0])["status"] == "ok":
|
|
104
121
|
break
|
|
105
|
-
# Request user authentication
|
|
106
|
-
self.authenticate_user()
|
|
107
122
|
|
|
108
123
|
def authenticate_user(self):
|
|
109
124
|
print("\n" + "=" * 50)
|
|
@@ -208,32 +223,46 @@ class TelegramExporter(BaseExporter):
|
|
|
208
223
|
def send_api_request(self, method, data=None, files=None):
|
|
209
224
|
"""Send a request to the Telegram Bot API."""
|
|
210
225
|
url = f"https://api.telegram.org/bot{self.bot_token}/{method}"
|
|
226
|
+
retry_delay = 10 # Start with 10 seconds
|
|
227
|
+
max_retry_delay = 120 # Cap at 2 minutes
|
|
211
228
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
229
|
+
while True:
|
|
230
|
+
try:
|
|
231
|
+
if files:
|
|
232
|
+
response = self.session.post(
|
|
233
|
+
url, data=data, files=files, timeout=60
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
response = self.session.post(url, json=data, timeout=30)
|
|
217
237
|
|
|
218
|
-
|
|
238
|
+
response_data = response.json()
|
|
219
239
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
240
|
+
if not response_data.get("ok"):
|
|
241
|
+
error_message = response_data.get("description", "Unknown error")
|
|
242
|
+
self.logger.error(f"Telegram API error: {error_message}")
|
|
223
243
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
244
|
+
# Handle rate limiting
|
|
245
|
+
if "retry_after" in response_data:
|
|
246
|
+
retry_after = response_data["retry_after"]
|
|
247
|
+
self.logger.info(
|
|
248
|
+
f"Rate limited. Waiting for {retry_after} seconds"
|
|
249
|
+
)
|
|
250
|
+
time.sleep(retry_after + 1)
|
|
251
|
+
return self.send_api_request(method, data, files)
|
|
230
252
|
|
|
231
|
-
|
|
253
|
+
raise Exception(f"Telegram API error: {error_message}")
|
|
232
254
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
255
|
+
return response_data["result"]
|
|
256
|
+
except requests.exceptions.RequestException as e:
|
|
257
|
+
if "Connection reset by peer" in str(e):
|
|
258
|
+
self.logger.warning(
|
|
259
|
+
f"Connection reset by peer. Retrying in {retry_delay} seconds..."
|
|
260
|
+
)
|
|
261
|
+
time.sleep(retry_delay)
|
|
262
|
+
retry_delay = min(retry_delay * 2, max_retry_delay)
|
|
263
|
+
continue
|
|
264
|
+
self.logger.error(f"Request error: {e}")
|
|
265
|
+
raise
|
|
237
266
|
|
|
238
267
|
def get_message_link(self, chat_id, message_id, username=None):
|
|
239
268
|
"""Generate a link to a Telegram message."""
|
|
@@ -851,66 +880,82 @@ class TelegramExporter(BaseExporter):
|
|
|
851
880
|
channel_result = self.extract_id_from_link(self.args.tgchannel)
|
|
852
881
|
chat_result = self.extract_id_from_link(self.args.tgchat)
|
|
853
882
|
|
|
854
|
-
#
|
|
883
|
+
# First, try to resolve both IDs without user interaction
|
|
884
|
+
channel_id = None
|
|
885
|
+
chat_id = None
|
|
886
|
+
needs_channel_interaction = False
|
|
887
|
+
needs_chat_interaction = False
|
|
888
|
+
|
|
855
889
|
if isinstance(channel_result, int):
|
|
856
890
|
channel_id = channel_result
|
|
857
891
|
elif isinstance(channel_result, str):
|
|
858
892
|
channel_id = self.resolve_username_to_id(channel_result)
|
|
859
893
|
if not channel_id:
|
|
860
|
-
|
|
861
|
-
print("Please forward any message from the target channel to the bot.")
|
|
862
|
-
print("This will allow me to extract the channel ID automatically.")
|
|
863
|
-
print("=" * 50 + "\n")
|
|
864
|
-
|
|
865
|
-
# Wait for a forwarded message with channel information
|
|
866
|
-
channel_id = self.wait_for_forwarded_message(
|
|
867
|
-
entity_type="channel", check_type=True
|
|
868
|
-
)
|
|
869
|
-
if channel_id:
|
|
870
|
-
self.save_username(channel_result, channel_id)
|
|
871
|
-
else:
|
|
872
|
-
raise Exception("Failed to get channel ID from forwarded message")
|
|
894
|
+
needs_channel_interaction = True
|
|
873
895
|
else:
|
|
874
896
|
raise Exception("Channel ID is undefined")
|
|
875
897
|
|
|
876
|
-
# Handle chat resolution
|
|
877
898
|
if isinstance(chat_result, int):
|
|
878
899
|
chat_id = chat_result
|
|
879
900
|
elif isinstance(chat_result, str):
|
|
880
901
|
chat_id = self.resolve_username_to_id(chat_result)
|
|
881
902
|
if not chat_id:
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
)
|
|
886
|
-
print("This will allow me to extract the group ID automatically.")
|
|
887
|
-
print(
|
|
888
|
-
"The bot MUST be added do the group and made admin, else it won't work!"
|
|
889
|
-
)
|
|
890
|
-
print("=" * 50 + "\n")
|
|
903
|
+
needs_chat_interaction = True
|
|
904
|
+
else:
|
|
905
|
+
raise Exception("Chat ID is undefined")
|
|
891
906
|
|
|
892
|
-
|
|
907
|
+
# Only authenticate if we need user interaction
|
|
908
|
+
if needs_channel_interaction or needs_chat_interaction:
|
|
909
|
+
self.authenticate_user()
|
|
910
|
+
|
|
911
|
+
# Handle channel resolution with user interaction if needed
|
|
912
|
+
if needs_channel_interaction:
|
|
913
|
+
print("\n" + "=" * 50)
|
|
914
|
+
print("Please forward any message from the target channel to the bot.")
|
|
915
|
+
print("This will allow me to extract the channel ID automatically.")
|
|
916
|
+
print("=" * 50 + "\n")
|
|
917
|
+
|
|
918
|
+
# Wait for a forwarded message with channel information
|
|
919
|
+
channel_id = self.wait_for_forwarded_message(
|
|
920
|
+
entity_type="channel", check_type=True
|
|
921
|
+
)
|
|
922
|
+
if channel_id:
|
|
923
|
+
self.save_username(channel_result, channel_id)
|
|
924
|
+
else:
|
|
925
|
+
raise Exception("Failed to get channel ID from forwarded message")
|
|
926
|
+
|
|
927
|
+
# Handle chat resolution with user interaction if needed
|
|
928
|
+
if needs_chat_interaction:
|
|
929
|
+
print("\n" + "=" * 50)
|
|
930
|
+
print(
|
|
931
|
+
f"Please write a message in the discussion group with text: {self.chat_auth_uuid}"
|
|
932
|
+
)
|
|
933
|
+
print("This will allow me to extract the group ID automatically.")
|
|
934
|
+
print(
|
|
935
|
+
"The bot MUST be added do the group and made admin, else it won't work!"
|
|
936
|
+
)
|
|
937
|
+
print("=" * 50 + "\n")
|
|
938
|
+
|
|
939
|
+
# Wait for a forwarded message with chat information
|
|
940
|
+
chat_id = self.wait_for_forwarded_message(
|
|
941
|
+
entity_type="chat", check_type=False
|
|
942
|
+
)
|
|
943
|
+
if not chat_id:
|
|
944
|
+
self.logger.error("Failed to get chat ID from forwarded message")
|
|
945
|
+
return False
|
|
946
|
+
while chat_id == channel_id:
|
|
947
|
+
error_msg = (
|
|
948
|
+
"Chat ID and channel ID are the same. The problem may be that "
|
|
949
|
+
"you posted a message in the channel, not in the discussion group."
|
|
950
|
+
)
|
|
951
|
+
self.logger.error(error_msg)
|
|
893
952
|
chat_id = self.wait_for_forwarded_message(
|
|
894
|
-
entity_type="chat",
|
|
953
|
+
entity_type="chat",
|
|
954
|
+
check_type=False,
|
|
955
|
+
add_msg=error_msg,
|
|
895
956
|
)
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
return False
|
|
899
|
-
while chat_id == channel_id:
|
|
900
|
-
error_msg = (
|
|
901
|
-
"Chat ID and channel ID are the same. The problem may be that "
|
|
902
|
-
"you posted a message in the channel, not in the discussion group."
|
|
903
|
-
)
|
|
904
|
-
self.logger.error(error_msg)
|
|
905
|
-
chat_id = self.wait_for_forwarded_message(
|
|
906
|
-
entity_type="chat",
|
|
907
|
-
check_type=False,
|
|
908
|
-
add_msg=error_msg,
|
|
909
|
-
)
|
|
910
|
-
if chat_id:
|
|
911
|
-
self.save_username(chat_result, chat_id)
|
|
912
|
-
else:
|
|
913
|
-
raise Exception("Chat ID is undefined")
|
|
957
|
+
if chat_id:
|
|
958
|
+
self.save_username(chat_result, chat_id)
|
|
914
959
|
|
|
915
960
|
if not channel_id:
|
|
916
961
|
raise Exception("Channel ID is undefined")
|
|
@@ -1234,14 +1279,15 @@ class TelegramExporter(BaseExporter):
|
|
|
1234
1279
|
return None
|
|
1235
1280
|
|
|
1236
1281
|
def verify_access(self, telegram_id, hr_type=None):
|
|
1237
|
-
url = f"https://api.telegram.org/bot{self.bot_token}/getChatAdministrators"
|
|
1238
1282
|
if not str(telegram_id).startswith("-100"):
|
|
1239
1283
|
telegram_id = f"-100{telegram_id}"
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1284
|
+
try:
|
|
1285
|
+
result = self.send_api_request(
|
|
1286
|
+
"getChatAdministrators", {"chat_id": telegram_id}
|
|
1287
|
+
)
|
|
1288
|
+
if self.args.debug:
|
|
1289
|
+
print(f"getChatAdministrators result: {result}")
|
|
1290
|
+
admin_ids = {x["user"]["id"] for x in result}
|
|
1291
|
+
return self.bot_id in admin_ids
|
|
1292
|
+
except Exception as e:
|
|
1293
|
+
raise Exception(f"Bot isn't added to {hr_type}: {e}")
|
|
@@ -47,7 +47,7 @@ from chgksuite.typotools import re_url
|
|
|
47
47
|
from chgksuite.typotools import remove_excessive_whitespace as rew
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
SEP =
|
|
50
|
+
SEP = "\n"
|
|
51
51
|
EDITORS = {
|
|
52
52
|
"win32": "notepad",
|
|
53
53
|
"linux2": "xdg-open", # python2
|
|
@@ -916,6 +916,9 @@ class UnknownEncodingException(Exception):
|
|
|
916
916
|
|
|
917
917
|
def chgk_parse_txt(txtfile, encoding=None, defaultauthor="", args=None, logger=None):
|
|
918
918
|
raw = open(txtfile, "rb").read()
|
|
919
|
+
# Fix corrupted line endings at byte level before decoding
|
|
920
|
+
if b"\r\r\n" in raw:
|
|
921
|
+
raw = raw.replace(b"\r\r\n", b"\n")
|
|
919
922
|
if not encoding:
|
|
920
923
|
if chardet.detect(raw)["confidence"] > 0.7:
|
|
921
924
|
encoding = chardet.detect(raw)["encoding"]
|
|
@@ -926,9 +929,10 @@ def chgk_parse_txt(txtfile, encoding=None, defaultauthor="", args=None, logger=N
|
|
|
926
929
|
"or resave with a less exotic encoding".format(txtfile)
|
|
927
930
|
)
|
|
928
931
|
text = raw.decode(encoding)
|
|
929
|
-
|
|
932
|
+
# Normalize any remaining line endings
|
|
933
|
+
text = text.replace("\r\n", "\n").replace("\r", "\n")
|
|
930
934
|
if text[0:10] == "Чемпионат:":
|
|
931
|
-
return chgk_parse_db(text
|
|
935
|
+
return chgk_parse_db(text, debug=args.debug, logger=logger)
|
|
932
936
|
return chgk_parse(text.replace("_", "\\_"), defaultauthor=defaultauthor, args=args)
|
|
933
937
|
|
|
934
938
|
|
|
@@ -15,6 +15,7 @@ from chgksuite.common import (
|
|
|
15
15
|
get_lastdir,
|
|
16
16
|
get_source_dirs,
|
|
17
17
|
log_wrap,
|
|
18
|
+
read_text_file,
|
|
18
19
|
set_lastdir,
|
|
19
20
|
)
|
|
20
21
|
|
|
@@ -50,9 +51,7 @@ def upload_file(filepath, trello, list_name=None):
|
|
|
50
51
|
raise Exception(f"list '{list_name}' not found")
|
|
51
52
|
assert lid is not None
|
|
52
53
|
print(f"uploading to list '{list_['name']}'")
|
|
53
|
-
content =
|
|
54
|
-
with open(filepath, "r", encoding="utf-8") as f:
|
|
55
|
-
content = f.read()
|
|
54
|
+
content = read_text_file(filepath)
|
|
56
55
|
cards = re.split(r"(\r?\n){2,}", content)
|
|
57
56
|
cards = [x for x in cards if x != "" and x != "\n" and x != "\r\n"]
|
|
58
57
|
for card in cards:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.28.0"
|
|
@@ -53,6 +53,13 @@ chgksuite = "chgksuite.__main__:main"
|
|
|
53
53
|
[tool.hatch.version]
|
|
54
54
|
path = "chgksuite/version.py"
|
|
55
55
|
|
|
56
|
+
[tool.hatch.build.targets.sdist]
|
|
57
|
+
include = [
|
|
58
|
+
"chgksuite/**/*",
|
|
59
|
+
"/LICENSE",
|
|
60
|
+
"/README.md",
|
|
61
|
+
]
|
|
62
|
+
|
|
56
63
|
[tool.hatch.build.targets.wheel]
|
|
57
64
|
packages = ["chgksuite"]
|
|
58
65
|
|