rgwfuncs 0.0.103__tar.gz → 0.0.107__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.
- {rgwfuncs-0.0.103/src/rgwfuncs.egg-info → rgwfuncs-0.0.107}/PKG-INFO +2 -1
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/pyproject.toml +3 -2
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/setup.cfg +2 -1
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs/__init__.py +1 -1
- rgwfuncs-0.0.107/src/rgwfuncs/str_lib.py +276 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107/src/rgwfuncs.egg-info}/PKG-INFO +2 -1
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs.egg-info/requires.txt +1 -0
- rgwfuncs-0.0.103/src/rgwfuncs/str_lib.py +0 -106
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/LICENSE +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/README.md +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs/df_lib.py +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs/docs_lib.py +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs/interactive_shell_lib.py +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs.egg-info/SOURCES.txt +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs.egg-info/dependency_links.txt +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs.egg-info/entry_points.txt +0 -0
- {rgwfuncs-0.0.103 → rgwfuncs-0.0.107}/src/rgwfuncs.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rgwfuncs
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.107
|
4
4
|
Summary: A functional programming paradigm for mathematical modelling and data science
|
5
5
|
Home-page: https://github.com/ryangerardwilson/rgwfuncs
|
6
6
|
Author: Ryan Gerard Wilson
|
@@ -24,6 +24,7 @@ Requires-Dist: requests
|
|
24
24
|
Requires-Dist: slack-sdk
|
25
25
|
Requires-Dist: google-api-python-client
|
26
26
|
Requires-Dist: boto3
|
27
|
+
Requires-Dist: pyfiglet
|
27
28
|
Dynamic: license-file
|
28
29
|
|
29
30
|
# RGWFUNCS
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "rgwfuncs"
|
7
|
-
version = "0.0.
|
7
|
+
version = "0.0.107"
|
8
8
|
authors = [
|
9
9
|
{ name = "Ryan Gerard Wilson", email = "ryangerardwilson@gmail.com" },
|
10
10
|
]
|
@@ -27,7 +27,8 @@ dependencies = [
|
|
27
27
|
"requests",
|
28
28
|
"slack-sdk",
|
29
29
|
"google-api-python-client",
|
30
|
-
"boto3"
|
30
|
+
"boto3",
|
31
|
+
"pyfiglet"
|
31
32
|
]
|
32
33
|
|
33
34
|
dynamic = ["scripts"]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[metadata]
|
2
2
|
name = rgwfuncs
|
3
|
-
version = 0.0.
|
3
|
+
version = 0.0.107
|
4
4
|
author = Ryan Gerard Wilson
|
5
5
|
author_email = ryangerardwilson@gmail.com
|
6
6
|
description = A functional programming paradigm for mathematical modelling and data science
|
@@ -29,6 +29,7 @@ install_requires =
|
|
29
29
|
slack-sdk
|
30
30
|
google-api-python-client
|
31
31
|
boto3
|
32
|
+
pyfiglet
|
32
33
|
|
33
34
|
[options.packages.find]
|
34
35
|
where = src
|
@@ -4,4 +4,4 @@
|
|
4
4
|
from .df_lib import append_columns, append_percentile_classification_column, append_ranged_classification_column, append_ranged_date_classification_column, append_rows, append_xgb_labels, append_xgb_logistic_regression_predictions, append_xgb_regression_predictions, bag_union_join, bottom_n_unique_values, cascade_sort, delete_rows, drop_duplicates, drop_duplicates_retain_first, drop_duplicates_retain_last, filter_dataframe, filter_indian_mobiles, first_n_rows, from_raw_data, insert_dataframe_in_sqlite_database, last_n_rows, left_join, limit_dataframe, load_data_from_path, load_data_from_query, load_data_from_sqlite_path, load_fresh_data_or_pull_from_cache, mask_against_dataframe, mask_against_dataframe_converse, numeric_clean, order_columns, print_correlation, print_dataframe, print_memory_usage, print_n_frequency_cascading, print_n_frequency_linear, rename_columns, retain_columns, right_join, send_data_to_email, send_data_to_slack, send_dataframe_via_telegram, sync_dataframe_to_sqlite_database, top_n_unique_values, union_join, update_rows
|
5
5
|
from .interactive_shell_lib import interactive_shell
|
6
6
|
from .docs_lib import docs
|
7
|
-
from .str_lib import send_telegram_message
|
7
|
+
from .str_lib import heading, send_telegram_message, sub_heading, title
|
@@ -0,0 +1,276 @@
|
|
1
|
+
import sys
|
2
|
+
import time
|
3
|
+
import os
|
4
|
+
import json
|
5
|
+
import requests
|
6
|
+
from typing import Tuple, Optional, Union, Dict
|
7
|
+
from collections import defaultdict
|
8
|
+
import warnings
|
9
|
+
from pyfiglet import Figlet
|
10
|
+
from datetime import datetime
|
11
|
+
|
12
|
+
# Suppress all FutureWarnings
|
13
|
+
warnings.filterwarnings("ignore", category=FutureWarning)
|
14
|
+
|
15
|
+
# Module-level variables
|
16
|
+
_PRINT_HEADING_CURRENT_CALL = 0
|
17
|
+
_PRINT_SUBHEADING_COUNTS = defaultdict(int) # Tracks sub-headings per heading
|
18
|
+
_CURRENT_HEADING_NUMBER = 0
|
19
|
+
|
20
|
+
def send_telegram_message(preset_name: str, message: str, config: Optional[Union[str, dict]] = None) -> None:
|
21
|
+
"""
|
22
|
+
Send a Telegram message using the specified preset.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
preset_name (str): The name of the preset to use for sending the message.
|
26
|
+
message (str): The message to send.
|
27
|
+
config (Optional[Union[str, dict]], optional): Configuration source. Can be:
|
28
|
+
- None: Searches for '.rgwfuncsrc' in current directory and upwards
|
29
|
+
- str: Path to a JSON configuration file
|
30
|
+
- dict: Direct configuration dictionary
|
31
|
+
|
32
|
+
Raises:
|
33
|
+
FileNotFoundError: If no '.rgwfuncsrc' file is found in current or parent directories.
|
34
|
+
ValueError: If the config parameter is neither a path string nor a dictionary, or if the config file is empty/invalid.
|
35
|
+
RuntimeError: If the preset is not found or necessary details are missing.
|
36
|
+
"""
|
37
|
+
def get_config(config: Optional[Union[str, dict]] = None) -> dict:
|
38
|
+
"""Get configuration either from a path, direct dictionary, or by searching upwards."""
|
39
|
+
def get_config_from_file(config_path: str) -> dict:
|
40
|
+
"""Load configuration from a JSON file."""
|
41
|
+
# print(f"Reading config from: {config_path}") # Debug line
|
42
|
+
with open(config_path, 'r', encoding='utf-8') as file:
|
43
|
+
content = file.read()
|
44
|
+
# print(f"Config content (first 100 chars): {content[:100]}...") # Debug line
|
45
|
+
if not content.strip():
|
46
|
+
raise ValueError(f"Config file {config_path} is empty")
|
47
|
+
try:
|
48
|
+
return json.loads(content)
|
49
|
+
except json.JSONDecodeError as e:
|
50
|
+
raise ValueError(f"Invalid JSON in config file {config_path}: {e}")
|
51
|
+
|
52
|
+
def find_config_file() -> str:
|
53
|
+
"""Search for '.rgwfuncsrc' in current directory and upwards."""
|
54
|
+
current_dir = os.getcwd()
|
55
|
+
# print(f"Starting config search from: {current_dir}") # Debug line
|
56
|
+
while True:
|
57
|
+
config_path = os.path.join(current_dir, '.rgwfuncsrc')
|
58
|
+
# print(f"Checking for config at: {config_path}") # Debug line
|
59
|
+
if os.path.isfile(config_path):
|
60
|
+
# print(f"Found config at: {config_path}") # Debug line
|
61
|
+
return config_path
|
62
|
+
parent_dir = os.path.dirname(current_dir)
|
63
|
+
if parent_dir == current_dir: # Reached root directory
|
64
|
+
raise FileNotFoundError(f"No '.rgwfuncsrc' file found in {os.getcwd()} or parent directories")
|
65
|
+
current_dir = parent_dir
|
66
|
+
|
67
|
+
# Determine the config to use
|
68
|
+
if config is None:
|
69
|
+
# Search for .rgwfuncsrc upwards from current directory
|
70
|
+
config_path = find_config_file()
|
71
|
+
return get_config_from_file(config_path)
|
72
|
+
elif isinstance(config, str):
|
73
|
+
# If config is a string, treat it as a path and load it
|
74
|
+
return get_config_from_file(config)
|
75
|
+
elif isinstance(config, dict):
|
76
|
+
# If config is already a dict, use it directly
|
77
|
+
return config
|
78
|
+
else:
|
79
|
+
raise ValueError("Config must be either a path string or a dictionary")
|
80
|
+
|
81
|
+
def get_telegram_preset(config: dict, preset_name: str) -> dict:
|
82
|
+
"""Get the Telegram preset configuration."""
|
83
|
+
presets = config.get("telegram_bot_presets", [])
|
84
|
+
for preset in presets:
|
85
|
+
if preset.get("name") == preset_name:
|
86
|
+
return preset
|
87
|
+
return None
|
88
|
+
|
89
|
+
def get_telegram_bot_details(config: dict, preset_name: str) -> Tuple[str, str]:
|
90
|
+
"""Retrieve the Telegram bot token and chat ID from the preset."""
|
91
|
+
preset = get_telegram_preset(config, preset_name)
|
92
|
+
if not preset:
|
93
|
+
raise RuntimeError(
|
94
|
+
f"Telegram bot preset '{preset_name}' not found in the configuration file")
|
95
|
+
|
96
|
+
bot_token = preset.get("bot_token")
|
97
|
+
chat_id = preset.get("chat_id")
|
98
|
+
|
99
|
+
if not bot_token or not chat_id:
|
100
|
+
raise RuntimeError(
|
101
|
+
f"Telegram bot token or chat ID for '{preset_name}' not found in the configuration file")
|
102
|
+
|
103
|
+
return bot_token, chat_id
|
104
|
+
|
105
|
+
config = get_config(config)
|
106
|
+
# Get bot details from the configuration
|
107
|
+
bot_token, chat_id = get_telegram_bot_details(config, preset_name)
|
108
|
+
|
109
|
+
# Prepare the request
|
110
|
+
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
|
111
|
+
payload = {"chat_id": chat_id, "text": message}
|
112
|
+
|
113
|
+
# Send the message
|
114
|
+
response = requests.post(url, json=payload)
|
115
|
+
response.raise_for_status()
|
116
|
+
|
117
|
+
def title(text: str, font: str = "slant", typing_speed: float = 0.005) -> None:
|
118
|
+
"""
|
119
|
+
Print text as ASCII art with a typewriter effect using the specified font (default: slant),
|
120
|
+
indented by 4 spaces. All output, including errors and separators, is printed with the
|
121
|
+
typewriter effect within this function.
|
122
|
+
|
123
|
+
Args:
|
124
|
+
text (str): The text to convert to ASCII art.
|
125
|
+
font (str, optional): The pyfiglet font to use. Defaults to "slant".
|
126
|
+
typing_speed (float, optional): Delay between printing each character in seconds.
|
127
|
+
Defaults to 0.005.
|
128
|
+
|
129
|
+
Raises:
|
130
|
+
ValueError: If the specified font is invalid or unavailable.
|
131
|
+
RuntimeError: If there is an error generating the ASCII art.
|
132
|
+
"""
|
133
|
+
# ANSI color codes
|
134
|
+
heading_color = '\033[92m' # Bright green for headings
|
135
|
+
reset_color = '\033[0m' # Reset to default
|
136
|
+
|
137
|
+
try:
|
138
|
+
# Initialize Figlet with the specified font
|
139
|
+
figlet = Figlet(font=font)
|
140
|
+
# Generate ASCII art
|
141
|
+
ascii_art = figlet.renderText(text)
|
142
|
+
# Indent each line by 4 spaces
|
143
|
+
indented_ascii_art = '\n'.join(' ' + line for line in ascii_art.splitlines())
|
144
|
+
|
145
|
+
# Print ASCII art with typewriter effect
|
146
|
+
print(heading_color, end='')
|
147
|
+
for char in indented_ascii_art + '\n':
|
148
|
+
print(char, end='', flush=True)
|
149
|
+
if char != '\n': # Don't delay on newlines
|
150
|
+
time.sleep(typing_speed)
|
151
|
+
print(reset_color, end='')
|
152
|
+
|
153
|
+
# Print separator line with typewriter effect
|
154
|
+
print(heading_color, end='')
|
155
|
+
for char in '=' * 79 + '\n':
|
156
|
+
print(char, end='', flush=True)
|
157
|
+
if char != '\n':
|
158
|
+
time.sleep(typing_speed)
|
159
|
+
print(reset_color, end='')
|
160
|
+
|
161
|
+
except Exception as e:
|
162
|
+
error_msg = ''
|
163
|
+
if "font" in str(e).lower():
|
164
|
+
error_msg = f"Invalid or unavailable font: {font}. Ensure the font is supported by pyfiglet.\n"
|
165
|
+
print(reset_color, end='')
|
166
|
+
for char in error_msg:
|
167
|
+
print(char, end='', flush=True)
|
168
|
+
if char != '\n':
|
169
|
+
time.sleep(typing_speed)
|
170
|
+
raise ValueError(error_msg)
|
171
|
+
error_msg = f"Error generating ASCII art for \"{text}\" with font {font}: {e}\n"
|
172
|
+
print(reset_color, end='')
|
173
|
+
for char in error_msg:
|
174
|
+
print(char, end='', flush=True)
|
175
|
+
if char != '\n':
|
176
|
+
time.sleep(typing_speed)
|
177
|
+
raise RuntimeError(error_msg)
|
178
|
+
|
179
|
+
def heading(text: str, typing_speed: float = 0.002) -> None:
|
180
|
+
"""
|
181
|
+
Print a heading with the specified text in uppercase,
|
182
|
+
formatted as '[current_call] TEXT' with a timestamp and typewriter effect.
|
183
|
+
Ensures the formatted heading is <= 50 characters and total line is 79 characters.
|
184
|
+
Adds empty lines before and after the heading.
|
185
|
+
|
186
|
+
Args:
|
187
|
+
text (str): The heading text to print.
|
188
|
+
typing_speed (float, optional): Delay between printing each character in seconds.
|
189
|
+
Defaults to 0.005.
|
190
|
+
"""
|
191
|
+
global _PRINT_HEADING_CURRENT_CALL, _CURRENT_HEADING_NUMBER, _PRINT_SUBHEADING_COUNTS
|
192
|
+
|
193
|
+
# Increment heading call
|
194
|
+
_PRINT_HEADING_CURRENT_CALL += 1
|
195
|
+
_CURRENT_HEADING_NUMBER = _PRINT_HEADING_CURRENT_CALL
|
196
|
+
_PRINT_SUBHEADING_COUNTS[_CURRENT_HEADING_NUMBER] = 0 # Reset sub-heading count
|
197
|
+
|
198
|
+
# ANSI color codes
|
199
|
+
color = '\033[92m' # Bright green for headings
|
200
|
+
reset_color = '\033[0m'
|
201
|
+
|
202
|
+
# Format heading
|
203
|
+
prefix = f"[{_PRINT_HEADING_CURRENT_CALL}] "
|
204
|
+
max_text_length = 50 - len(prefix)
|
205
|
+
formatted_text = text.upper()[:max_text_length]
|
206
|
+
heading = f"{prefix}{formatted_text}"
|
207
|
+
|
208
|
+
# Get timestamp
|
209
|
+
timestamp = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]"
|
210
|
+
|
211
|
+
# Calculate padding
|
212
|
+
padding_length = 79 - len(heading) - 1 - len(timestamp) - 1 # Spaces before padding and timestamp
|
213
|
+
padding = '=' * padding_length if padding_length > 0 else ''
|
214
|
+
full_line = f"{heading} {padding} {timestamp}"
|
215
|
+
|
216
|
+
# Print with line breaks and typewriter effect
|
217
|
+
print() # Empty line before
|
218
|
+
print(color, end='')
|
219
|
+
for char in full_line + '\n':
|
220
|
+
print(char, end='', flush=True)
|
221
|
+
if char != '\n':
|
222
|
+
time.sleep(typing_speed)
|
223
|
+
print(reset_color, end='')
|
224
|
+
print() # Empty line after
|
225
|
+
|
226
|
+
def sub_heading(text: str, typing_speed: float = 0.002) -> None:
|
227
|
+
"""
|
228
|
+
Print a sub-heading under the most recent heading, formatted as
|
229
|
+
'[heading_num.sub_heading_num] TEXT' with a timestamp and typewriter effect.
|
230
|
+
Ensures the formatted sub-heading is <= 50 characters and total line is 79 characters.
|
231
|
+
Adds empty lines before and after the sub-heading.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
text (str): The sub-heading text to print.
|
235
|
+
typing_speed (float, optional): Delay between printing each character in seconds.
|
236
|
+
Defaults to 0.005.
|
237
|
+
|
238
|
+
Raises:
|
239
|
+
ValueError: If no heading has been called.
|
240
|
+
"""
|
241
|
+
global _PRINT_SUBHEADING_COUNTS, _CURRENT_HEADING_NUMBER
|
242
|
+
|
243
|
+
if _CURRENT_HEADING_NUMBER == 0:
|
244
|
+
raise ValueError("No heading called before sub_heading.")
|
245
|
+
|
246
|
+
# Increment sub-heading count
|
247
|
+
_PRINT_SUBHEADING_COUNTS[_CURRENT_HEADING_NUMBER] += 1
|
248
|
+
current_sub = _PRINT_SUBHEADING_COUNTS[_CURRENT_HEADING_NUMBER]
|
249
|
+
|
250
|
+
# ANSI color codes
|
251
|
+
color = '\033[92m' # Bright green for sub-headings
|
252
|
+
reset_color = '\033[0m'
|
253
|
+
|
254
|
+
# Format sub-heading
|
255
|
+
prefix = f"[{_CURRENT_HEADING_NUMBER}.{current_sub}] "
|
256
|
+
max_text_length = 50 - len(prefix)
|
257
|
+
formatted_text = text.lower()[:max_text_length]
|
258
|
+
sub_heading = f"{prefix}{formatted_text}"
|
259
|
+
|
260
|
+
# Get timestamp
|
261
|
+
timestamp = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]"
|
262
|
+
|
263
|
+
# Calculate padding
|
264
|
+
padding_length = 79 - len(sub_heading) - 1 - len(timestamp) - 1 # Spaces before padding and timestamp
|
265
|
+
padding = '-' * padding_length if padding_length > 0 else ''
|
266
|
+
full_line = f"{sub_heading} {padding} {timestamp}"
|
267
|
+
|
268
|
+
# Print with line breaks and typewriter effect
|
269
|
+
print() # Empty line before
|
270
|
+
print(color, end='')
|
271
|
+
for char in full_line + '\n':
|
272
|
+
print(char, end='', flush=True)
|
273
|
+
if char != '\n':
|
274
|
+
time.sleep(typing_speed)
|
275
|
+
print(reset_color, end='')
|
276
|
+
print() # Empty line after
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: rgwfuncs
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.107
|
4
4
|
Summary: A functional programming paradigm for mathematical modelling and data science
|
5
5
|
Home-page: https://github.com/ryangerardwilson/rgwfuncs
|
6
6
|
Author: Ryan Gerard Wilson
|
@@ -24,6 +24,7 @@ Requires-Dist: requests
|
|
24
24
|
Requires-Dist: slack-sdk
|
25
25
|
Requires-Dist: google-api-python-client
|
26
26
|
Requires-Dist: boto3
|
27
|
+
Requires-Dist: pyfiglet
|
27
28
|
Dynamic: license-file
|
28
29
|
|
29
30
|
# RGWFUNCS
|
@@ -1,106 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import json
|
3
|
-
import requests
|
4
|
-
from typing import Tuple, Optional, Union, Dict
|
5
|
-
import warnings
|
6
|
-
|
7
|
-
# Suppress all FutureWarnings
|
8
|
-
warnings.filterwarnings("ignore", category=FutureWarning)
|
9
|
-
|
10
|
-
|
11
|
-
def send_telegram_message(preset_name: str, message: str, config: Optional[Union[str, dict]] = None) -> None:
|
12
|
-
"""
|
13
|
-
Send a Telegram message using the specified preset.
|
14
|
-
|
15
|
-
Args:
|
16
|
-
preset_name (str): The name of the preset to use for sending the message.
|
17
|
-
message (str): The message to send.
|
18
|
-
config (Optional[Union[str, dict]], optional): Configuration source. Can be:
|
19
|
-
- None: Searches for '.rgwfuncsrc' in current directory and upwards
|
20
|
-
- str: Path to a JSON configuration file
|
21
|
-
- dict: Direct configuration dictionary
|
22
|
-
|
23
|
-
Raises:
|
24
|
-
FileNotFoundError: If no '.rgwfuncsrc' file is found in current or parent directories.
|
25
|
-
ValueError: If the config parameter is neither a path string nor a dictionary, or if the config file is empty/invalid.
|
26
|
-
RuntimeError: If the preset is not found or necessary details are missing.
|
27
|
-
"""
|
28
|
-
def get_config(config: Optional[Union[str, dict]] = None) -> dict:
|
29
|
-
"""Get configuration either from a path, direct dictionary, or by searching upwards."""
|
30
|
-
def get_config_from_file(config_path: str) -> dict:
|
31
|
-
"""Load configuration from a JSON file."""
|
32
|
-
# print(f"Reading config from: {config_path}") # Debug line
|
33
|
-
with open(config_path, 'r', encoding='utf-8') as file:
|
34
|
-
content = file.read()
|
35
|
-
# print(f"Config content (first 100 chars): {content[:100]}...") # Debug line
|
36
|
-
if not content.strip():
|
37
|
-
raise ValueError(f"Config file {config_path} is empty")
|
38
|
-
try:
|
39
|
-
return json.loads(content)
|
40
|
-
except json.JSONDecodeError as e:
|
41
|
-
raise ValueError(f"Invalid JSON in config file {config_path}: {e}")
|
42
|
-
|
43
|
-
def find_config_file() -> str:
|
44
|
-
"""Search for '.rgwfuncsrc' in current directory and upwards."""
|
45
|
-
current_dir = os.getcwd()
|
46
|
-
# print(f"Starting config search from: {current_dir}") # Debug line
|
47
|
-
while True:
|
48
|
-
config_path = os.path.join(current_dir, '.rgwfuncsrc')
|
49
|
-
# print(f"Checking for config at: {config_path}") # Debug line
|
50
|
-
if os.path.isfile(config_path):
|
51
|
-
# print(f"Found config at: {config_path}") # Debug line
|
52
|
-
return config_path
|
53
|
-
parent_dir = os.path.dirname(current_dir)
|
54
|
-
if parent_dir == current_dir: # Reached root directory
|
55
|
-
raise FileNotFoundError(f"No '.rgwfuncsrc' file found in {os.getcwd()} or parent directories")
|
56
|
-
current_dir = parent_dir
|
57
|
-
|
58
|
-
# Determine the config to use
|
59
|
-
if config is None:
|
60
|
-
# Search for .rgwfuncsrc upwards from current directory
|
61
|
-
config_path = find_config_file()
|
62
|
-
return get_config_from_file(config_path)
|
63
|
-
elif isinstance(config, str):
|
64
|
-
# If config is a string, treat it as a path and load it
|
65
|
-
return get_config_from_file(config)
|
66
|
-
elif isinstance(config, dict):
|
67
|
-
# If config is already a dict, use it directly
|
68
|
-
return config
|
69
|
-
else:
|
70
|
-
raise ValueError("Config must be either a path string or a dictionary")
|
71
|
-
|
72
|
-
def get_telegram_preset(config: dict, preset_name: str) -> dict:
|
73
|
-
"""Get the Telegram preset configuration."""
|
74
|
-
presets = config.get("telegram_bot_presets", [])
|
75
|
-
for preset in presets:
|
76
|
-
if preset.get("name") == preset_name:
|
77
|
-
return preset
|
78
|
-
return None
|
79
|
-
|
80
|
-
def get_telegram_bot_details(config: dict, preset_name: str) -> Tuple[str, str]:
|
81
|
-
"""Retrieve the Telegram bot token and chat ID from the preset."""
|
82
|
-
preset = get_telegram_preset(config, preset_name)
|
83
|
-
if not preset:
|
84
|
-
raise RuntimeError(
|
85
|
-
f"Telegram bot preset '{preset_name}' not found in the configuration file")
|
86
|
-
|
87
|
-
bot_token = preset.get("bot_token")
|
88
|
-
chat_id = preset.get("chat_id")
|
89
|
-
|
90
|
-
if not bot_token or not chat_id:
|
91
|
-
raise RuntimeError(
|
92
|
-
f"Telegram bot token or chat ID for '{preset_name}' not found in the configuration file")
|
93
|
-
|
94
|
-
return bot_token, chat_id
|
95
|
-
|
96
|
-
config = get_config(config)
|
97
|
-
# Get bot details from the configuration
|
98
|
-
bot_token, chat_id = get_telegram_bot_details(config, preset_name)
|
99
|
-
|
100
|
-
# Prepare the request
|
101
|
-
url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
|
102
|
-
payload = {"chat_id": chat_id, "text": message}
|
103
|
-
|
104
|
-
# Send the message
|
105
|
-
response = requests.post(url, json=payload)
|
106
|
-
response.raise_for_status()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|