chgksuite 0.27.2__py3-none-any.whl → 0.28.0__py3-none-any.whl

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/cli.py CHANGED
@@ -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 ("caption", "advanced", "argtype", "hide", "filetypes"):
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,
@@ -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
- req_me = requests.get(f"https://api.telegram.org/bot{self.bot_token}/getMe")
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! {obj}")
59
- self.bot_id = obj["result"]["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
- try:
213
- if files:
214
- response = requests.post(url, data=data, files=files, timeout=60)
215
- else:
216
- response = requests.post(url, json=data, timeout=30)
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
- response_data = response.json()
238
+ response_data = response.json()
219
239
 
220
- if not response_data.get("ok"):
221
- error_message = response_data.get("description", "Unknown error")
222
- self.logger.error(f"Telegram API error: {error_message}")
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
- # Handle rate limiting
225
- if "retry_after" in response_data:
226
- retry_after = response_data["retry_after"]
227
- self.logger.info(f"Rate limited. Waiting for {retry_after} seconds")
228
- time.sleep(retry_after + 1)
229
- return self.send_api_request(method, data, files)
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
- raise Exception(f"Telegram API error: {error_message}")
253
+ raise Exception(f"Telegram API error: {error_message}")
232
254
 
233
- return response_data["result"]
234
- except requests.exceptions.RequestException as e:
235
- self.logger.error(f"Request error: {e}")
236
- raise
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
- # Handle channel resolution
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
- print("\n" + "=" * 50)
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
- print("\n" + "=" * 50)
883
- print(
884
- f"Please write a message in the discussion group with text: {self.chat_auth_uuid}"
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
- # Wait for a forwarded message with chat information
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", check_type=False
953
+ entity_type="chat",
954
+ check_type=False,
955
+ add_msg=error_msg,
895
956
  )
896
- if not chat_id:
897
- self.logger.error("Failed to get chat ID from forwarded message")
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
- req = requests.post(url, data={"chat_id": telegram_id})
1241
- if self.args.debug:
1242
- print(req.status_code, req.text)
1243
- if req.status_code != 200:
1244
- raise Exception(f"Bot isn't added to {hr_type}")
1245
- obj = req.json()
1246
- admin_ids = {x["user"]["id"] for x in obj["result"]}
1247
- return self.bot_id in admin_ids
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}")
chgksuite/parser.py CHANGED
@@ -38,7 +38,6 @@ from chgksuite.common import (
38
38
  init_logger,
39
39
  load_settings,
40
40
  log_wrap,
41
- read_text_file,
42
41
  set_lastdir,
43
42
  )
44
43
  from chgksuite.composer import gui_compose
chgksuite/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.27.2"
1
+ __version__ = "0.28.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chgksuite
3
- Version: 0.27.2
3
+ Version: 0.28.0
4
4
  Summary: A package for chgk automation
5
5
  Project-URL: Homepage, https://gitlab.com/peczony/chgksuite
6
6
  Author-email: Alexander Pecheny <ap@pecheny.me>
@@ -1,14 +1,14 @@
1
1
  chgksuite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  chgksuite/__main__.py,sha256=0-_jfloveTW3SZYW5XEagbyaHKGCiDhGNgcLxsT_dMs,140
3
3
  chgksuite/_html2md.py,sha256=IzPlRo4dVZNVdlnoCQFjSEfvpFZ0KdvVzfKSeIt15lM,2454
4
- chgksuite/cli.py,sha256=fHa7HNJeQeUNXpbqnMnSXHOMpbxpaph6d3KdVNx9uNg,41257
4
+ chgksuite/cli.py,sha256=B081O0Ats_gCJO4yVA6CeJ5x3apu_rvQQmZGKOWFQ-U,41657
5
5
  chgksuite/common.py,sha256=27HEx5Us0xXywUAWtjlkhsXs67-Cx7ylJSlUViUsOgU,11778
6
6
  chgksuite/lastdir,sha256=BbZVRYZnXBQzJUrl7a2e4xuUcfmq6asNI705pTxBfD4,49
7
- chgksuite/parser.py,sha256=JJ_koqLDHAl-IHUe4sTyOK2gySL6uVW5HJmO1xcqD8Q,46042
7
+ chgksuite/parser.py,sha256=G9AkqEd2nSOGOWhCxpJpZmxNzO8aoij7gL6utJAzo_Q,46022
8
8
  chgksuite/parser_db.py,sha256=Ngh2ZYhAyetb6Sa-5xC9aX8quX9Ar1WheSfhSy-JADw,11105
9
9
  chgksuite/trello.py,sha256=hc_RMlWrNrPzg9AsgC8KE9ow3JUbetMJM_HuiJlBSIk,14693
10
10
  chgksuite/typotools.py,sha256=J2AEQbfcR0HHSu5WCALpj4Ya_ngOMXRh7esJgcybWQM,12797
11
- chgksuite/version.py,sha256=_CerHyxnUJ2hU0sB7noT3JBjjI0ohEM5boAPBoFxOg4,23
11
+ chgksuite/version.py,sha256=MRQGtOXBhcDKeeNOL0LiB-cllo6kfd8_KGJOvaDp0XQ,23
12
12
  chgksuite/vulture_whitelist.py,sha256=P__p_X0zt10ivddIf81uyxsobV14vFg8uS2lt4foYpc,3582
13
13
  chgksuite/composer/__init__.py,sha256=a02nOz1QqPi84621-v3JD0Nn1_M0_fHyzKqG5Wp2ahI,6511
14
14
  chgksuite/composer/chgksuite_parser.py,sha256=ItlTenviFDuqP-f1960nzD-gRPFDQy4RdOL39PswTvg,9044
@@ -21,7 +21,7 @@ chgksuite/composer/markdown.py,sha256=hWajBAvop_BTyBI6rzeIwAaj7p_6p2kQre4WDMalD9
21
21
  chgksuite/composer/openquiz.py,sha256=BF506tH6b1IoocC61l5xBU969l38S1IjqMi6mqhG_HI,6990
22
22
  chgksuite/composer/pptx.py,sha256=9O0tfx2xpyx9Y4ceatVIXdiwvslnj8gliZIlxsDe5Ow,23971
23
23
  chgksuite/composer/stats.py,sha256=GbraSrjaZ8Mc2URs5aGAsI4ekboAKzlJJOqsbe96ELA,3995
24
- chgksuite/composer/telegram.py,sha256=KM9Bnkf2bxdJNMrhjCCw2xx-sWcIQioBc1FdSY4OX-g,47431
24
+ chgksuite/composer/telegram.py,sha256=lOykDAeOyYn6NJe8b33clWZdm83yqpsWFql0VyAhXzc,48862
25
25
  chgksuite/composer/telegram_bot.py,sha256=xT5D39m4zGmIbHV_ZfyQ9Rc8PAmG2V5FGUeDKpkgyTw,3767
26
26
  chgksuite/handouter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  chgksuite/handouter/gen.py,sha256=CJfGl8R2vnElnsjBo0n4u-y8KD-l2qxrIuUHj5WZBuI,5241
@@ -56,8 +56,8 @@ chgksuite/resources/regexes_uz_cyr.json,sha256=D4AyaEPEY753I47Ky2Fwol_4kxQsl-Yu9
56
56
  chgksuite/resources/template.docx,sha256=Do29TAsg3YbH0rRSaXhVzKEoh4pwXkklW_idWA34HVE,11189
57
57
  chgksuite/resources/template.pptx,sha256=hEFWqE-yYpwZ8ejrMCJIPEyoMT3eDqaqtiEeQ7I4fyk,29777
58
58
  chgksuite/resources/trello.json,sha256=M5Q9JR-AAJF1u16YtNAxDX-7c7VoVTXuq4POTqYvq8o,555
59
- chgksuite-0.27.2.dist-info/METADATA,sha256=zGPoXuzOtUiNMdE7NDeSJE25K6XHaoQ4pPLBkfBLz_c,1152
60
- chgksuite-0.27.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
61
- chgksuite-0.27.2.dist-info/entry_points.txt,sha256=lqjX6ULQZGDt0rgouTXBuwEPiwKkDQkSiNsT877A_Jg,54
62
- chgksuite-0.27.2.dist-info/licenses/LICENSE,sha256=_a1yfntuPmctLsuiE_08xMSORuCfGS8X5hQph2U_PUw,1081
63
- chgksuite-0.27.2.dist-info/RECORD,,
59
+ chgksuite-0.28.0.dist-info/METADATA,sha256=sHAtIIRiyBXxgQp8lfUl8-NPRdjwd5h4YC1SQR_lp_o,1152
60
+ chgksuite-0.28.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
61
+ chgksuite-0.28.0.dist-info/entry_points.txt,sha256=lqjX6ULQZGDt0rgouTXBuwEPiwKkDQkSiNsT877A_Jg,54
62
+ chgksuite-0.28.0.dist-info/licenses/LICENSE,sha256=_a1yfntuPmctLsuiE_08xMSORuCfGS8X5hQph2U_PUw,1081
63
+ chgksuite-0.28.0.dist-info/RECORD,,