telegram-signer 1.0.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.
Files changed (28) hide show
  1. telegram-signer-1.0.0/LICENSE +29 -0
  2. telegram-signer-1.0.0/PKG-INFO +182 -0
  3. telegram-signer-1.0.0/README.md +168 -0
  4. telegram-signer-1.0.0/pyproject.toml +79 -0
  5. telegram-signer-1.0.0/setup.cfg +4 -0
  6. telegram-signer-1.0.0/telegram_signer/__init__.py +2 -0
  7. telegram-signer-1.0.0/telegram_signer/__main__.py +10 -0
  8. telegram-signer-1.0.0/telegram_signer/_kurigram/__init__.py +4 -0
  9. telegram-signer-1.0.0/telegram_signer/_kurigram/methods.py +77 -0
  10. telegram-signer-1.0.0/telegram_signer/ai_tools.py +206 -0
  11. telegram-signer-1.0.0/telegram_signer/cli/__init__.py +3 -0
  12. telegram-signer-1.0.0/telegram_signer/cli/signer.py +279 -0
  13. telegram-signer-1.0.0/telegram_signer/config.py +375 -0
  14. telegram-signer-1.0.0/telegram_signer/core.py +1417 -0
  15. telegram-signer-1.0.0/telegram_signer/logger.py +90 -0
  16. telegram-signer-1.0.0/telegram_signer/sign_record_store.py +350 -0
  17. telegram-signer-1.0.0/telegram_signer/utils.py +88 -0
  18. telegram-signer-1.0.0/telegram_signer.egg-info/PKG-INFO +182 -0
  19. telegram-signer-1.0.0/telegram_signer.egg-info/SOURCES.txt +26 -0
  20. telegram-signer-1.0.0/telegram_signer.egg-info/dependency_links.txt +1 -0
  21. telegram-signer-1.0.0/telegram_signer.egg-info/entry_points.txt +2 -0
  22. telegram-signer-1.0.0/telegram_signer.egg-info/requires.txt +7 -0
  23. telegram-signer-1.0.0/telegram_signer.egg-info/top_level.txt +4 -0
  24. telegram-signer-1.0.0/tests/test_cli_migrate_sign_records.py +68 -0
  25. telegram-signer-1.0.0/tests/test_core.py +1014 -0
  26. telegram-signer-1.0.0/tests/test_sign_config_v2_to_v3.py +179 -0
  27. telegram-signer-1.0.0/tests/test_sign_record_store.py +141 -0
  28. telegram-signer-1.0.0/tests/test_utils.py +116 -0
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Amchii
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.1
2
+ Name: telegram-signer
3
+ Version: 1.0.0
4
+ Summary: Telegram check-in scheduler with keyboard clicks and optional AI-assisted captcha handling.
5
+ Author: Telegram Signer Contributors
6
+ License: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/git-buster/telegram-signer
8
+ Project-URL: Repository, https://github.com/git-buster/telegram-signer
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+
15
+ # Telegram Signer
16
+
17
+ Telegram Signer is a Python CLI tool for scheduled Telegram check-ins. It can send
18
+ configured check-in messages, click inline keyboard buttons, handle topic IDs, keep
19
+ SQLite check-in records, and optionally use an OpenAI-compatible API for image or
20
+ math-style verification steps.
21
+
22
+ This fork is intentionally focused on check-ins only. It does not include extra
23
+ dashboard or message-watching features.
24
+
25
+ ## Features
26
+
27
+ - Required account name in every command: `telegram-signer <account> ...`
28
+ - Scheduled check-in tasks with cron-style or time-style schedules
29
+ - Legacy config compatibility for existing signer `config.json` files
30
+ - SQLite check-in record storage with lazy migration from `sign_record.json`
31
+ - Telegram forum topic support through `message_thread_id`
32
+ - Action flow support:
33
+ - send text
34
+ - send dice
35
+ - click a button by text
36
+ - choose an option by image through an LLM
37
+ - reply to a calculation prompt through an LLM
38
+ - click captcha buttons according to a slot-machine dice result
39
+
40
+ ## Installation
41
+
42
+ Requires Python 3.10 or newer.
43
+
44
+ ```sh
45
+ pip install -U telegram-signer
46
+ ```
47
+
48
+ ## Command Format
49
+
50
+ The account name is required and comes before the command:
51
+
52
+ ```sh
53
+ telegram-signer account_a login
54
+ telegram-signer account_a logout
55
+ telegram-signer account_a run task_name
56
+ ```
57
+
58
+ Run multiple accounts with your shell by starting separate processes. Keep this to
59
+ five accounts or fewer:
60
+
61
+ ```sh
62
+ telegram-signer account_a run task_a &
63
+ telegram-signer account_b run task_b &
64
+ telegram-signer account_c run task_c &
65
+ ```
66
+
67
+ ## Common Commands
68
+
69
+ ```sh
70
+ telegram-signer account_a login
71
+ telegram-signer account_a logout
72
+ telegram-signer account_a list
73
+ telegram-signer account_a reconfig task_name
74
+ telegram-signer account_a run task_name
75
+ telegram-signer account_a run-force task_name
76
+ telegram-signer account_a list-records task_name
77
+ telegram-signer account_a migrate-records
78
+ telegram-signer account_a llm-config
79
+ ```
80
+
81
+ Global options must be placed before the account name:
82
+
83
+ ```sh
84
+ telegram-signer --workdir .signer account_a run task_name
85
+ ```
86
+
87
+ ## Config Compatibility
88
+
89
+ Existing signer configs remain supported. The default path is:
90
+
91
+ ```text
92
+ <workdir>/telegram-signer/config/<task_name>/config.json
93
+ ```
94
+
95
+ Older signer config versions are still migrated through the existing compatibility
96
+ chain. Existing configs from the older path are still read and copied to the new
97
+ path when loaded:
98
+
99
+ ```text
100
+ <workdir>/signs/<task_name>/config.json
101
+ ```
102
+
103
+ Check-in records use SQLite at:
104
+
105
+ ```text
106
+ <workdir>/data.sqlite3
107
+ ```
108
+
109
+ Legacy record files are still readable and can be migrated:
110
+
111
+ ```text
112
+ <workdir>/signs/<task_name>/<user_id>/sign_record.json
113
+ <workdir>/signs/<task_name>/sign_record.json
114
+ ```
115
+
116
+ ## Example Config
117
+
118
+ ```json
119
+ {
120
+ "chats": [
121
+ {
122
+ "chat_id": "@target_bot_or_group",
123
+ "message_thread_id": null,
124
+ "name": "daily check-in",
125
+ "delete_after": null,
126
+ "actions": [
127
+ {"action": 1, "text": "/checkin"},
128
+ {"action": 3, "text": "Check in"},
129
+ {"action": 6, "if_dice_emoji": "🎰"}
130
+ ],
131
+ "action_interval": 1
132
+ }
133
+ ],
134
+ "sign_at": "0 6 * * *",
135
+ "random_seconds": 0,
136
+ "sign_interval": 1
137
+ }
138
+ ```
139
+
140
+ Action IDs:
141
+
142
+ | ID | Action |
143
+ | --- | --- |
144
+ | 1 | send text |
145
+ | 2 | send dice |
146
+ | 3 | click keyboard button by text |
147
+ | 4 | choose option by image |
148
+ | 5 | reply to calculation prompt |
149
+ | 6 | solve slot-machine captcha buttons when an `if_*` condition matches |
150
+
151
+ Slot-machine captcha solving is intentionally conditional. Add at least one of
152
+ these fields to an `action: 6` step:
153
+
154
+ ```json
155
+ {"action": 6, "if_dice_emoji": "🎰"}
156
+ {"action": 6, "if_text": "choose the symbols"}
157
+ {"action": 6, "if_regex": "slot|captcha|verification"}
158
+ ```
159
+
160
+ If no `if_*` condition is present, the action is skipped.
161
+
162
+ ## LLM Configuration
163
+
164
+ LLM-backed actions read configuration from either environment variables or the
165
+ workdir config file created by `llm-config`.
166
+
167
+ Environment variables:
168
+
169
+ ```text
170
+ OPENAI_API_KEY
171
+ OPENAI_BASE_URL
172
+ OPENAI_MODEL
173
+ ```
174
+
175
+ Persistent workdir file:
176
+
177
+ ```text
178
+ <workdir>/.openai_config.json
179
+ ```
180
+
181
+ Do not commit sessions, session strings, `.env` files, `.openai_config.json`, logs,
182
+ or local SQLite databases.
@@ -0,0 +1,168 @@
1
+ # Telegram Signer
2
+
3
+ Telegram Signer is a Python CLI tool for scheduled Telegram check-ins. It can send
4
+ configured check-in messages, click inline keyboard buttons, handle topic IDs, keep
5
+ SQLite check-in records, and optionally use an OpenAI-compatible API for image or
6
+ math-style verification steps.
7
+
8
+ This fork is intentionally focused on check-ins only. It does not include extra
9
+ dashboard or message-watching features.
10
+
11
+ ## Features
12
+
13
+ - Required account name in every command: `telegram-signer <account> ...`
14
+ - Scheduled check-in tasks with cron-style or time-style schedules
15
+ - Legacy config compatibility for existing signer `config.json` files
16
+ - SQLite check-in record storage with lazy migration from `sign_record.json`
17
+ - Telegram forum topic support through `message_thread_id`
18
+ - Action flow support:
19
+ - send text
20
+ - send dice
21
+ - click a button by text
22
+ - choose an option by image through an LLM
23
+ - reply to a calculation prompt through an LLM
24
+ - click captcha buttons according to a slot-machine dice result
25
+
26
+ ## Installation
27
+
28
+ Requires Python 3.10 or newer.
29
+
30
+ ```sh
31
+ pip install -U telegram-signer
32
+ ```
33
+
34
+ ## Command Format
35
+
36
+ The account name is required and comes before the command:
37
+
38
+ ```sh
39
+ telegram-signer account_a login
40
+ telegram-signer account_a logout
41
+ telegram-signer account_a run task_name
42
+ ```
43
+
44
+ Run multiple accounts with your shell by starting separate processes. Keep this to
45
+ five accounts or fewer:
46
+
47
+ ```sh
48
+ telegram-signer account_a run task_a &
49
+ telegram-signer account_b run task_b &
50
+ telegram-signer account_c run task_c &
51
+ ```
52
+
53
+ ## Common Commands
54
+
55
+ ```sh
56
+ telegram-signer account_a login
57
+ telegram-signer account_a logout
58
+ telegram-signer account_a list
59
+ telegram-signer account_a reconfig task_name
60
+ telegram-signer account_a run task_name
61
+ telegram-signer account_a run-force task_name
62
+ telegram-signer account_a list-records task_name
63
+ telegram-signer account_a migrate-records
64
+ telegram-signer account_a llm-config
65
+ ```
66
+
67
+ Global options must be placed before the account name:
68
+
69
+ ```sh
70
+ telegram-signer --workdir .signer account_a run task_name
71
+ ```
72
+
73
+ ## Config Compatibility
74
+
75
+ Existing signer configs remain supported. The default path is:
76
+
77
+ ```text
78
+ <workdir>/telegram-signer/config/<task_name>/config.json
79
+ ```
80
+
81
+ Older signer config versions are still migrated through the existing compatibility
82
+ chain. Existing configs from the older path are still read and copied to the new
83
+ path when loaded:
84
+
85
+ ```text
86
+ <workdir>/signs/<task_name>/config.json
87
+ ```
88
+
89
+ Check-in records use SQLite at:
90
+
91
+ ```text
92
+ <workdir>/data.sqlite3
93
+ ```
94
+
95
+ Legacy record files are still readable and can be migrated:
96
+
97
+ ```text
98
+ <workdir>/signs/<task_name>/<user_id>/sign_record.json
99
+ <workdir>/signs/<task_name>/sign_record.json
100
+ ```
101
+
102
+ ## Example Config
103
+
104
+ ```json
105
+ {
106
+ "chats": [
107
+ {
108
+ "chat_id": "@target_bot_or_group",
109
+ "message_thread_id": null,
110
+ "name": "daily check-in",
111
+ "delete_after": null,
112
+ "actions": [
113
+ {"action": 1, "text": "/checkin"},
114
+ {"action": 3, "text": "Check in"},
115
+ {"action": 6, "if_dice_emoji": "🎰"}
116
+ ],
117
+ "action_interval": 1
118
+ }
119
+ ],
120
+ "sign_at": "0 6 * * *",
121
+ "random_seconds": 0,
122
+ "sign_interval": 1
123
+ }
124
+ ```
125
+
126
+ Action IDs:
127
+
128
+ | ID | Action |
129
+ | --- | --- |
130
+ | 1 | send text |
131
+ | 2 | send dice |
132
+ | 3 | click keyboard button by text |
133
+ | 4 | choose option by image |
134
+ | 5 | reply to calculation prompt |
135
+ | 6 | solve slot-machine captcha buttons when an `if_*` condition matches |
136
+
137
+ Slot-machine captcha solving is intentionally conditional. Add at least one of
138
+ these fields to an `action: 6` step:
139
+
140
+ ```json
141
+ {"action": 6, "if_dice_emoji": "🎰"}
142
+ {"action": 6, "if_text": "choose the symbols"}
143
+ {"action": 6, "if_regex": "slot|captcha|verification"}
144
+ ```
145
+
146
+ If no `if_*` condition is present, the action is skipped.
147
+
148
+ ## LLM Configuration
149
+
150
+ LLM-backed actions read configuration from either environment variables or the
151
+ workdir config file created by `llm-config`.
152
+
153
+ Environment variables:
154
+
155
+ ```text
156
+ OPENAI_API_KEY
157
+ OPENAI_BASE_URL
158
+ OPENAI_MODEL
159
+ ```
160
+
161
+ Persistent workdir file:
162
+
163
+ ```text
164
+ <workdir>/.openai_config.json
165
+ ```
166
+
167
+ Do not commit sessions, session strings, `.env` files, `.openai_config.json`, logs,
168
+ or local SQLite databases.
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "telegram-signer"
7
+ dynamic = ["version"]
8
+ description = "Telegram check-in scheduler with keyboard clicks and optional AI-assisted captcha handling."
9
+ authors = [
10
+ { name = "Telegram Signer Contributors" }
11
+ ]
12
+ license = { text = "BSD-3-Clause" }
13
+ readme = "README.md"
14
+ requires-python = ">=3.10"
15
+ classifiers = [
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3 :: Only"
18
+ ]
19
+ dependencies = [
20
+ "kurigram>=2.2.19,<2.3.0",
21
+ "click",
22
+ "pydantic",
23
+ "openai",
24
+ "croniter",
25
+ "json_repair",
26
+ "typing-extensions",
27
+ ]
28
+
29
+ [dependency-groups]
30
+ dev = [
31
+ "ruff>=0.14.6",
32
+ "tox>=4.32.0",
33
+ "pytest>=9.0.1",
34
+ "pytest-asyncio>=1.3.0",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/git-buster/telegram-signer"
39
+ Repository = "https://github.com/git-buster/telegram-signer"
40
+
41
+ [project.scripts]
42
+ telegram-signer = "telegram_signer.__main__:signer"
43
+
44
+ [tool.setuptools]
45
+ include-package-data = true
46
+
47
+ [tool.setuptools.dynamic]
48
+ version = { attr = "telegram_signer.__version__" }
49
+
50
+ [tool.setuptools.packages.find]
51
+ exclude = ["tests*"]
52
+
53
+ [tool.ruff]
54
+ line-length = 88
55
+
56
+ [tool.ruff.lint]
57
+ extend-select = [
58
+ "I", # isort
59
+ "B", # flake8-bugbear
60
+ "W", # pycodestyle
61
+ "C4", # flake8-comprehensions
62
+ ]
63
+ ignore = [
64
+ "E501", # line too long, handled by black
65
+ "B008", # do not perform function calls in argument defaults
66
+ "C901", # too complex
67
+ "W191", # indentation contains tabs
68
+ "B019", # Use of `functools.lru_cache` or `functools.cache` on methods can lead to memory leaks
69
+ "B904" # raise without from inside except
70
+ ]
71
+
72
+ [tool.pytest.ini_options]
73
+ asyncio_default_fixture_loop_scope = "function"
74
+ addopts = [
75
+ "-vv",
76
+ "-x",
77
+ "-W=ignore::DeprecationWarning:pyrogram.utils"
78
+ ]
79
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,2 @@
1
+ __version__ = "1.0.0"
2
+
@@ -0,0 +1,10 @@
1
+ __all__ = ("signer",)
2
+
3
+ import sys
4
+
5
+
6
+ def signer():
7
+ from telegram_signer import cli
8
+
9
+ sys.exit(cli.telegram_signer())
10
+
@@ -0,0 +1,4 @@
1
+ from .methods import SafeGetForumTopics
2
+
3
+ __all__ = ["SafeGetForumTopics"]
4
+
@@ -0,0 +1,77 @@
1
+ from typing import AsyncGenerator, Union
2
+
3
+ import pyrogram
4
+ from pyrogram import raw, types, utils
5
+
6
+
7
+ class SafeGetForumTopics:
8
+ """Temporary workaround for Kurigram forum topic pagination.
9
+
10
+ Kurigram 2.2.19 may raise AttributeError when the last topic in a page has
11
+ no ``top_message``. Keep this patch isolated so it can be removed once the
12
+ upstream method is fixed.
13
+ """
14
+
15
+ async def get_forum_topics(
16
+ self: "pyrogram.Client",
17
+ chat_id: Union[int, str],
18
+ limit: int = 0,
19
+ ) -> AsyncGenerator["types.ForumTopic", None]:
20
+ current = 0
21
+ total = limit or (1 << 31) - 1
22
+ limit = min(100, total)
23
+
24
+ offset_date = 0
25
+ offset_id = 0
26
+ offset_topic = 0
27
+ seen_topic_ids = set()
28
+
29
+ while True:
30
+ result = await self.invoke(
31
+ raw.functions.messages.GetForumTopics(
32
+ peer=await self.resolve_peer(chat_id),
33
+ offset_date=offset_date,
34
+ offset_id=offset_id,
35
+ offset_topic=offset_topic,
36
+ limit=limit,
37
+ )
38
+ )
39
+
40
+ users = {item.id: item for item in result.users}
41
+ chats = {item.id: item for item in result.chats}
42
+ messages = {}
43
+
44
+ for message in result.messages:
45
+ if isinstance(message, raw.types.MessageEmpty):
46
+ continue
47
+ messages[message.id] = await types.Message._parse(
48
+ self, message, users, chats
49
+ )
50
+
51
+ page_topics = []
52
+ for topic in result.topics:
53
+ parsed_topic = types.ForumTopic._parse(
54
+ self, topic, messages, users, chats
55
+ )
56
+ if parsed_topic is None or parsed_topic.id in seen_topic_ids:
57
+ continue
58
+ seen_topic_ids.add(parsed_topic.id)
59
+ page_topics.append(parsed_topic)
60
+
61
+ if not page_topics:
62
+ return
63
+
64
+ for topic in page_topics:
65
+ yield topic
66
+ current += 1
67
+ if current >= total:
68
+ return
69
+
70
+ last_topic = page_topics[-1]
71
+ if last_topic.top_message is None or last_topic.top_message.date is None:
72
+ return
73
+
74
+ offset_id = last_topic.top_message.id
75
+ offset_date = utils.datetime_to_timestamp(last_topic.top_message.date)
76
+ offset_topic = last_topic.id
77
+