pytbox 0.0.4__py3-none-any.whl → 0.0.6__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.

Potentially problematic release.


This version of pytbox might be problematic. Click here for more details.

pytbox/utils/timeutils.py CHANGED
@@ -1,30 +1,15 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
-
4
- import time
3
+ import re
5
4
  import pytz
5
+ import time
6
6
  import datetime
7
+ from zoneinfo import ZoneInfo
7
8
  from typing import Literal
8
9
 
9
10
 
10
11
  class TimeUtils:
11
12
 
12
- @staticmethod
13
- def get_timestamp(now: bool=True) -> int:
14
- '''
15
- 获取时间戳
16
-
17
- Args:
18
- now (bool, optional): _description_. Defaults to True.
19
-
20
- Returns:
21
- _type_: _description_
22
- '''
23
- if now:
24
- return int(time.time())
25
- else:
26
- return int(time.time() * 1000)
27
-
28
13
  @staticmethod
29
14
  def get_time_object(now: bool=True):
30
15
  '''
@@ -50,4 +35,513 @@ class TimeUtils:
50
35
  if time_format == '%Y-%m-%d %H:%M:%S':
51
36
  return time_obj_with_offset.strftime("%Y-%m-%d %H:%M:%S")
52
37
  elif time_format == '%Y-%m-%dT%H:%M:%SZ':
53
- return time_obj_with_offset.strftime("%Y-%m-%dT%H:%M:%SZ")
38
+ return time_obj_with_offset.strftime("%Y-%m-%dT%H:%M:%SZ")
39
+
40
+ @staticmethod
41
+ def get_time_diff_hours(time1, time2):
42
+ """
43
+ 计算两个datetime对象之间的小时差
44
+
45
+ Args:
46
+ time1: 第一个datetime对象
47
+ time2: 第二个datetime对象
48
+
49
+ Returns:
50
+ float: 两个时间之间的小时差
51
+ """
52
+ if not time1 or not time2:
53
+ return 0
54
+
55
+ # 确保两个时间都有时区信息
56
+ if time1.tzinfo is None:
57
+ time1 = time1.replace(tzinfo=pytz.timezone('Asia/Shanghai'))
58
+ if time2.tzinfo is None:
59
+ time2 = time2.replace(tzinfo=pytz.timezone('Asia/Shanghai'))
60
+
61
+ # 计算时间差(秒)
62
+ time_diff_seconds = abs((time2 - time1).total_seconds())
63
+
64
+ # 转换为小时
65
+ time_diff_hours = time_diff_seconds / 3600
66
+
67
+ return time_diff_hours
68
+
69
+ @staticmethod
70
+ def convert_syslog_huawei_str_to_8601(timestr):
71
+ """
72
+ 将华为 syslog 格式的时间字符串(如 '2025-08-02T04:34:24+08:00')转换为 ISO8601 格式的 UTC 时间字符串。
73
+
74
+ Args:
75
+ timestr (str): 原始时间字符串,格式如 '2025-08-02T04:34:24+08:00'
76
+
77
+ Returns:
78
+ str: 转换后的 ISO8601 格式 UTC 时间字符串,如 '2025-08-01T20:34:24.000000Z'
79
+ """
80
+ if timestr is None:
81
+ return None
82
+ try:
83
+ # 解析带时区的时间字符串
84
+ dt: datetime.datetime = datetime.datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S%z")
85
+ # 转换为 UTC
86
+ dt_utc: datetime.datetime = dt.astimezone(datetime.timezone.utc)
87
+ # 格式化为 ISO8601 字符串(带微秒,Z 结尾)
88
+ iso8601_utc: str = dt_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
89
+ return iso8601_utc
90
+ except ValueError as e:
91
+ # 日志记录异常(此处仅简单打印,实际项目建议用 logging)
92
+ print(f"时间转换失败: {e}, 输入: {timestr}")
93
+ return None
94
+
95
+ @staticmethod
96
+ def convert_str_to_timestamp(timestr):
97
+ """
98
+ 将类似 '2025-04-16T00:08:28.000+0000' 格式的时间字符串转换为时间戳(秒级)。
99
+
100
+ Args:
101
+ timestr (str): 时间字符串,格式如 '2025-04-16T00:08:28.000+0000'
102
+
103
+ Returns:
104
+ int: 时间戳(秒级)
105
+ """
106
+ if timestr is None:
107
+ return None
108
+ # 兼容带毫秒和时区的ISO8601格式
109
+ # 先将+0000或+08:00等时区格式标准化为+00:00
110
+ timestr_fixed = re.sub(r'([+-]\d{2})(\d{2})$', r'\1:\2', timestr)
111
+
112
+ # 处理毫秒部分(.000),如果没有毫秒也能兼容
113
+ try:
114
+ dt = datetime.datetime.fromisoformat(timestr_fixed)
115
+ except ValueError:
116
+ if len(timestr_fixed) == 8:
117
+ dt = datetime.datetime.strptime(timestr_fixed, "%Y%m%d")
118
+ else:
119
+ # 如果没有毫秒部分
120
+ dt = datetime.datetime.strptime(timestr_fixed, "%Y-%m-%dT%H:%M:%S%z")
121
+ # 返回秒级时间戳
122
+ return int(dt.timestamp()) * 1000
123
+
124
+ @staticmethod
125
+ def convert_str_to_datetime_lg_backup(time_str: str):
126
+ """
127
+ 将 '7/10/2025 3:52:43 PM' 这种格式的时间字符串转换为时间戳(秒级)。
128
+
129
+ Args:
130
+ time_str (str): 时间字符串,格式如 '7/10/2025 3:52:43 PM'
131
+
132
+ Returns:
133
+ int: 时间戳(秒级)
134
+ """
135
+ if time_str is None:
136
+ return None
137
+ # 先将字符串转换为 datetime 对象
138
+ dt = datetime.datetime.strptime(time_str, "%m/%d/%Y %I:%M:%S %p")
139
+ # 返回秒级时间戳
140
+ return int(dt.timestamp()) * 1000
141
+
142
+ @staticmethod
143
+ def convert_timestamp_to_str(timestamp: int, time_format: Literal['%Y-%m-%d %H:%M:%S', '%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S.000Z']='%Y-%m-%d %H:%M:%S', timezone_offset: int=8):
144
+ '''
145
+ _summary_
146
+
147
+ Args:
148
+ timestamp (_type_): _description_
149
+
150
+ Returns:
151
+ _type_: _description_
152
+ '''
153
+ timestamp = int(timestamp)
154
+ if timestamp > 10000000000:
155
+ timestamp = timestamp / 1000
156
+ # 使用datetime模块的fromtimestamp方法将时间戳转换为datetime对象
157
+ dt_object = datetime.datetime.fromtimestamp(timestamp, tz=ZoneInfo(f'Etc/GMT-{timezone_offset}'))
158
+
159
+ # 使用strftime方法将datetime对象格式化为字符串
160
+ return dt_object.strftime(time_format)
161
+
162
+ @staticmethod
163
+ def timestamp_to_timestr_dida(timestamp):
164
+ # 将时间戳转换为 UTC 时间
165
+ utc_time = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
166
+
167
+ # 将 UTC 时间转换为指定时区(+08:00)
168
+ target_timezone = datetime.timezone(datetime.timedelta(hours=8))
169
+ local_time = utc_time.astimezone(target_timezone)
170
+
171
+ # 格式化为指定字符串
172
+ formatted_time = local_time.strftime('%Y-%m-%dT%H:%M:%S%z')
173
+
174
+ # 添加冒号到时区部分
175
+ formatted_time = formatted_time[:-2] + ':' + formatted_time[-2:]
176
+ return formatted_time
177
+
178
+ @staticmethod
179
+ def timestamp_to_datetime_obj(timestamp: int, timezone_offset: int=8):
180
+ return datetime.datetime.fromtimestamp(timestamp, tz=ZoneInfo(f'Etc/GMT-{timezone_offset}'))
181
+
182
+ @staticmethod
183
+ def datetime_obj_to_str(datetime_obj, add_timezone=False):
184
+ if add_timezone:
185
+ datetime_obj = datetime_obj.astimezone(pytz.timezone('Asia/Shanghai'))
186
+ return datetime_obj.strftime("%Y-%m-%d %H:%M:%S")
187
+
188
+ @staticmethod
189
+ def get_current_time(app: Literal['notion', 'dida365']):
190
+ if app == 'notion':
191
+ return datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
192
+ elif app == 'dida365':
193
+ return datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S%z")
194
+
195
+ @staticmethod
196
+ def get_current_date_str(output_format: Literal['%Y%m%d', '%Y-%m-%d']='%Y%m%d') -> str:
197
+ # 获取当前日期和时间
198
+ now = datetime.datetime.now()
199
+
200
+ # 格式化为字符串
201
+ current_date_str = now.strftime(output_format)
202
+ return current_date_str
203
+
204
+ @staticmethod
205
+ def get_date_n_days_from_now(n: int = 0, output_format: Literal['%Y%m%d', '%Y-%m-%d'] = '%Y%m%d') -> str:
206
+ """
207
+ 获取距离当前日期 N 天后的日期字符串。
208
+
209
+ Args:
210
+ n (int): 天数,可以为负数(表示 N 天前),默认为 0(即今天)。
211
+ output_format (Literal['%Y%m%d', '%Y-%m-%d']): 日期输出格式,默认为 '%Y%m%d'。
212
+
213
+ Returns:
214
+ str: 格式化后的日期字符串。
215
+
216
+ 示例:
217
+ >>> MyTime.get_date_n_days_from_now(1)
218
+ '20240620'
219
+ >>> MyTime.get_date_n_days_from_now(-1, '%Y-%m-%d')
220
+ '2024-06-18'
221
+ """
222
+ target_date = datetime.datetime.now() + datetime.timedelta(days=n)
223
+ return target_date.strftime(output_format)
224
+
225
+
226
+ @staticmethod
227
+ def get_yesterday_date_str() -> str:
228
+ # 获取昨天日期
229
+ yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
230
+ yesterday_date_str = yesterday.strftime("%Y%m%d")
231
+ return yesterday_date_str
232
+
233
+ @staticmethod
234
+ def get_last_month_date_str() -> str:
235
+ # 获取上个月日期
236
+ last_month = datetime.datetime.now() - datetime.timedelta(days=30)
237
+ last_month_date_str = last_month.strftime("%Y-%m")
238
+ return last_month_date_str
239
+
240
+ @staticmethod
241
+ def get_current_time_str() -> str:
242
+ # 获取当前日期和时间
243
+ now = datetime.datetime.now()
244
+
245
+ # 格式化为字符串
246
+ current_date_str = now.strftime("%Y%m%d_%H%M")
247
+ return current_date_str
248
+
249
+ @staticmethod
250
+ def get_timestamp(now: bool=True, last_minutes: int=0, unit: Literal['ms', 's']='ms') -> int:
251
+ '''
252
+ 获取当前时间戳, 减去 last_minutes 分钟
253
+ '''
254
+ if now:
255
+ if unit == 'ms':
256
+ return int(time.time()) * 1000
257
+ elif unit == 's':
258
+ return int(time.time())
259
+
260
+ if last_minutes == 0:
261
+ if unit == 'ms':
262
+ return int(time.time()) * 1000
263
+ elif unit == 's':
264
+ return int(time.time())
265
+ else:
266
+ if unit == 'ms':
267
+ return int(time.time()) * 1000 - last_minutes * 60 * 1000
268
+ elif unit == 's':
269
+ return int(time.time()) - last_minutes * 60
270
+
271
+ @staticmethod
272
+ def get_timestamp_tomorrow() -> int:
273
+ return int(time.time()) * 1000 + 24 * 60 * 60 * 1000
274
+
275
+ @staticmethod
276
+ def get_timestamp_last_day(last_days: int=0, unit: Literal['ms', 's']='ms') -> int:
277
+ if last_days == 0:
278
+ if unit == 'ms':
279
+ return int(time.time()) * 1000
280
+ elif unit == 's':
281
+ return int(time.time())
282
+ else:
283
+ return int(time.time()) * 1000 - last_days * 24 * 60 * 60 * 1000
284
+
285
+ @staticmethod
286
+ def get_today_timestamp() -> int:
287
+ return int(time.time()) * 1000
288
+
289
+ @staticmethod
290
+ def convert_str_to_datetime(time_str: str, app: Literal['lg_alert_trigger', 'lg_alert_resolved', 'mongo']='lg_alert_trigger') -> datetime.datetime:
291
+ """
292
+ 将字符串转换为datetime对象
293
+
294
+ Args:
295
+ time_str (str): 时间字符串
296
+ app (Literal['lg_alert_trigger', 'lg_alert_resolved', 'mongo'], optional): 应用类型. Defaults to 'lg_alert_trigger'.
297
+
298
+ Returns:
299
+ datetime.datetime: 带时区信息的datetime对象
300
+ """
301
+ if app == 'lg_alert_trigger':
302
+ time_obj = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
303
+ elif app == 'lg_alert_resolved':
304
+ time_obj = datetime.datetime.strptime(time_str, "%Y年%m月%d日 %H:%M:%S")
305
+ elif app == 'mongo':
306
+ time_obj = datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00'))
307
+ return time_obj # 已经包含时区信息,直接返回
308
+
309
+ # 对于没有时区信息的时间对象,设置为Asia/Shanghai时区
310
+ time_with_tz = time_obj.replace(tzinfo=ZoneInfo("Asia/Shanghai"))
311
+ return time_with_tz
312
+
313
+ @staticmethod
314
+ def convert_timeobj_to_str(timeobj: str=None, timezone_offset: int=8, time_format: Literal['%Y-%m-%d %H:%M:%S', '%Y-%m-%dT%H:%M:%SZ']='%Y-%m-%d %H:%M:%S'):
315
+ time_obj_with_offset = timeobj + datetime.timedelta(hours=timezone_offset)
316
+ if time_format == '%Y-%m-%d %H:%M:%S':
317
+ return time_obj_with_offset.strftime("%Y-%m-%d %H:%M:%S")
318
+ elif time_format == '%Y-%m-%dT%H:%M:%SZ':
319
+ return time_obj_with_offset.strftime("%Y-%m-%dT%H:%M:%SZ")
320
+
321
+ @staticmethod
322
+ def convert_timestamp_to_timeobj(timestamp: int) -> datetime.datetime:
323
+ """
324
+ 将时间戳转换为时间对象
325
+
326
+ Args:
327
+ timestamp (int): 时间戳,单位为秒或毫秒
328
+
329
+ Returns:
330
+ datetime.datetime: 转换后的时间对象,时区为 Asia/Shanghai
331
+ """
332
+ # 如果时间戳是毫秒,转换为秒
333
+ if len(str(timestamp)) > 10:
334
+ timestamp = timestamp / 1000
335
+
336
+ return datetime.datetime.fromtimestamp(timestamp, tz=ZoneInfo("Asia/Shanghai"))
337
+
338
+ @staticmethod
339
+ def convert_timeobj_to_timestamp(timeobj: datetime.datetime) -> int:
340
+ """
341
+ 将时间对象转换为时间戳
342
+
343
+ Args:
344
+ timeobj (datetime.datetime): 时间对象
345
+
346
+ Returns:
347
+ int: 时间戳,单位为秒或毫秒
348
+ """
349
+ return int(timeobj.timestamp())
350
+
351
+ @staticmethod
352
+ def convert_timeobj_add_timezone(timeobj: datetime.datetime, timezone_offset: int=8) -> datetime.datetime:
353
+ return timeobj + datetime.timedelta(hours=timezone_offset)
354
+
355
+ @staticmethod
356
+ def convert_mute_duration(mute_duration: str) -> datetime.datetime:
357
+ """将屏蔽时长字符串转换为带 Asia/Shanghai 时区的 ``datetime`` 对象。
358
+
359
+ 支持两类输入:
360
+ - 绝对时间: 例如 ``"2025-01-02 13:45"``,按本地上海时区解释。
361
+ - 相对时间: 形如 ``"10m"``、``"2h"``、``"1d"`` 分别表示分钟、小时、天。
362
+
363
+ Args:
364
+ mute_duration: 屏蔽时长字符串。
365
+
366
+ Returns:
367
+ datetime.datetime: 带 ``Asia/Shanghai`` 时区信息的时间点。
368
+
369
+ Raises:
370
+ ValueError: 当输入字符串无法被解析时抛出。
371
+ """
372
+ shanghai_tz = ZoneInfo("Asia/Shanghai")
373
+ now: datetime.datetime = datetime.datetime.now(tz=shanghai_tz)
374
+ mute_duration = mute_duration.strip()
375
+
376
+ # 绝对时间格式(按上海时区解释)
377
+ try:
378
+ abs_dt_naive = datetime.datetime.strptime(mute_duration, "%Y-%m-%d %H:%M")
379
+ return abs_dt_naive.replace(tzinfo=shanghai_tz)
380
+ except ValueError:
381
+ pass
382
+
383
+ # 相对时间格式
384
+ pattern = r"^(\d+)([dhm])$"
385
+ match = re.match(pattern, mute_duration)
386
+ if match:
387
+ value_str, unit = match.groups()
388
+ value = int(value_str)
389
+ if unit == "d":
390
+ return now + datetime.timedelta(days=value)
391
+ if unit == "h":
392
+ return now + datetime.timedelta(hours=value)
393
+ if unit == "m":
394
+ return now + datetime.timedelta(minutes=value)
395
+
396
+ raise ValueError(f"无法解析 mute_duration: {mute_duration}")
397
+
398
+ @staticmethod
399
+ def convert_mute_duration_to_str(mute_duration: str) -> str:
400
+ '''
401
+ 将屏蔽时长字符串转换为距离当前时间的标准描述,形如 '2d3h'。
402
+
403
+ 支持两类输入:
404
+ - 绝对时间: 'YYYY-MM-DD HH:MM'(按 Asia/Shanghai 解释)
405
+ - 相对时间: '10m'、'2h'、'1d'
406
+
407
+ 如果目标时间已过去,则返回 '0h'。
408
+
409
+ Args:
410
+ mute_duration (str): 屏蔽时长字符串。
411
+
412
+ Returns:
413
+ str: 与当前时间的距离描述,例如 '1d2h'、'3h'。
414
+ '''
415
+ shanghai_tz = ZoneInfo("Asia/Shanghai")
416
+ now: datetime.datetime = datetime.datetime.now(tz=shanghai_tz)
417
+
418
+ try:
419
+ target_time: datetime.datetime = TimeUtils.convert_mute_duration(mute_duration)
420
+ except ValueError:
421
+ # 兜底:直接尝试绝对时间格式
422
+ try:
423
+ abs_dt = datetime.datetime.strptime(mute_duration.strip(), "%Y-%m-%d %H:%M")
424
+ target_time = abs_dt.replace(tzinfo=shanghai_tz)
425
+ except ValueError:
426
+ return mute_duration
427
+
428
+ # 计算与现在的差值(仅面向未来的剩余时长)
429
+ delta_seconds: float = (target_time - now).total_seconds()
430
+ if delta_seconds <= 0:
431
+ return "0h"
432
+
433
+ days: int = int(delta_seconds // 86400)
434
+ hours: int = int((delta_seconds % 86400) // 3600)
435
+
436
+ parts: list[str] = []
437
+ if days > 0:
438
+ parts.append(f"{days}d")
439
+ if hours > 0:
440
+ parts.append(f"{hours}h")
441
+ if not parts:
442
+ # 小于 1 小时
443
+ parts.append("0h")
444
+
445
+ return "".join(parts)
446
+
447
+ @staticmethod
448
+ def is_work_time(start_hour: int=9, end_hour: int=18) -> bool:
449
+ '''
450
+ 判断是否为工作时间
451
+
452
+ Args:
453
+ start_hour (int, optional): 开始工作时间. Defaults to 9.
454
+ end_hour (int, optional): 结束工作时间. Defaults to 18.
455
+
456
+ Returns:
457
+ bool: 如果是工作时间, 返回 True, 否则返回 False
458
+ '''
459
+ current_time = datetime.datetime.now().time()
460
+ start = datetime.time(start_hour, 0, 0)
461
+ end = datetime.time(end_hour, 0, 0)
462
+
463
+ if start <= current_time <= end:
464
+ return True
465
+ else:
466
+ return False
467
+
468
+ @staticmethod
469
+ def is_work_day() -> bool:
470
+ '''
471
+ 判断是否为工作日
472
+
473
+ Returns:
474
+ bool: 如果是工作日, 返回 True, 否则返回 False
475
+ '''
476
+ from chinese_calendar import is_workday
477
+ date_now = datetime.datetime.date(datetime.datetime.now())
478
+ # print(date_now)
479
+ if is_workday(date_now):
480
+ return True
481
+ else:
482
+ return False
483
+
484
+ @staticmethod
485
+ def get_week_number(offset: int=5) -> int:
486
+ '''
487
+ 获取今天是哪一年的第几周
488
+
489
+ Returns:
490
+ int: 返回一个元组,包含(年份, 周数)
491
+ '''
492
+ now = datetime.datetime.now()
493
+ # isocalendar()方法返回一个元组,包含年份、周数和周几
494
+ _year_unused, week, _ = now.isocalendar()
495
+ return week - offset
496
+
497
+ @staticmethod
498
+ def get_week_day(timestamp: int=None, offset: int=0) -> int:
499
+ '''
500
+ 获取今天是周几
501
+
502
+ Returns:
503
+ int: 周几的数字表示,1表示周一,7表示周日
504
+ '''
505
+ if timestamp is None:
506
+ now = datetime.datetime.now()
507
+ else:
508
+ if timestamp > 10000000000:
509
+ timestamp = timestamp / 1000
510
+ now = datetime.datetime.fromtimestamp(timestamp)
511
+ # weekday()方法返回0-6的数字,0表示周一,6表示周日
512
+ return now.weekday() + 1 + offset
513
+
514
+ @staticmethod
515
+ def get_week_of_year(customer: Literal['lululemon', 'other']='other') -> tuple[int, int]:
516
+ '''
517
+ 获取今天是哪一年的第几周
518
+
519
+ Returns:
520
+ tuple[int, int]: 返回一个元组,包含(年份, 周数)
521
+ '''
522
+ now = datetime.datetime.now()
523
+ # isocalendar()方法返回一个元组,包含年份、周数和周几
524
+ _year, week, _ = now.isocalendar()
525
+ if customer == 'lululemon':
526
+ week = week - 5
527
+ return _year, week
528
+
529
+ @staticmethod
530
+ def get_last_month_start_and_end_time() -> tuple[datetime.datetime, datetime.datetime]:
531
+ '''
532
+ 获取上个月的开始和结束时间
533
+
534
+ Returns:
535
+ tuple[datetime.datetime, datetime.datetime]: 返回一个元组,包含上个月的开始和结束时间
536
+ '''
537
+ today = datetime.datetime.now()
538
+ # 获取当前月份的第一天
539
+ first_day_of_current_month = today.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
540
+ # 上个月1号的0:00分
541
+ start_time = first_day_of_current_month - datetime.timedelta(days=1)
542
+ start_time = start_time.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
543
+
544
+ # 获取上个月的最后一天
545
+ end_time = first_day_of_current_month - datetime.timedelta(days=1)
546
+ end_time = end_time.replace(hour=23, minute=59, second=59, microsecond=0)
547
+ return start_time.strftime("%Y-%m-%dT%H:%M:%SZ"), end_time.strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytbox
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: A collection of Python integrations and utilities (Feishu, Dida365, VictoriaMetrics, ...)
5
5
  Author-email: mingming hou <houm01@foxmail.com>
6
6
  License: MIT
@@ -8,13 +8,20 @@ Requires-Python: >=3.8
8
8
  Description-Content-Type: text/markdown
9
9
  Requires-Dist: requests>=2.0
10
10
  Requires-Dist: pydantic>=1.10
11
- Requires-Dist: onepassword-sdk>=0.3.1
12
11
  Requires-Dist: onepasswordconnectsdk>=1.0.0
12
+ Requires-Dist: loguru>=0.7.3
13
+ Requires-Dist: chinese_calendar>=1.10.0
14
+ Requires-Dist: click>=8.0.0
15
+ Requires-Dist: rich>=12.0.0
13
16
  Provides-Extra: dev
14
17
  Requires-Dist: pytest; extra == "dev"
15
18
  Requires-Dist: black; extra == "dev"
16
19
  Requires-Dist: ruff; extra == "dev"
17
20
  Requires-Dist: python-dotenv; extra == "dev"
21
+ Provides-Extra: cli
22
+ Requires-Dist: jinja2>=3.0.0; extra == "cli"
23
+ Requires-Dist: pyyaml>=6.0; extra == "cli"
24
+ Requires-Dist: toml>=0.10.0; extra == "cli"
18
25
 
19
26
  # PytBox
20
27
 
@@ -0,0 +1,39 @@
1
+ pytbox/base.py,sha256=_SpfeIiJE4xbQMsYghnMehcNzHP-mBfrKONX43b0OQk,1490
2
+ pytbox/cli.py,sha256=N775a0GK80IT2lQC2KRYtkZpIiu9UjavZmaxgNUgJhQ,160
3
+ pytbox/dida365.py,sha256=pUMPB9AyLZpTTbaz2LbtzdEpyjvuGf4YlRrCvM5sbJo,10545
4
+ pytbox/onepassword_connect.py,sha256=nD3xTl1ykQ4ct_dCRRF138gXCtk-phPfKYXuOn-P7Z8,3064
5
+ pytbox/onepassword_sa.py,sha256=08iUcYud3aEHuQcUsem9bWNxdXKgaxFbMy9yvtr-DZQ,6995
6
+ pytbox/alert/alert_handler.py,sha256=FePPQS4LyGphSJ0QMv0_pLWaXxEqsRlcTKMfUjtsNfk,5048
7
+ pytbox/alert/ping.py,sha256=g36X0U3U8ndZqfpVIcuoxJJ0X5gST3I_IwjTQC1roHA,779
8
+ pytbox/alicloud/sls.py,sha256=UR4GdI86dCKAFI2xt_1DELu7q743dpd3xrYtuNpfC5A,4065
9
+ pytbox/categraf/build_config.py,sha256=PQkxTgc8AORQJbkOrd-kJ8IC4USnp4IFQA9LZW0LBI4,1695
10
+ pytbox/cli/__init__.py,sha256=5ID4-oXrMsHFcfDsQeXDYeThPOuQ1Fl2x2kHWfgfOEw,67
11
+ pytbox/cli/main.py,sha256=S-DBp-1d0BCpvZ7jRE3bYmhKSiPpCJHFGsbdVF485BY,322
12
+ pytbox/cli/categraf/__init__.py,sha256=HfhDhWiWEuT5e6fXb6fs7UgoZPwn9WQ1wdFoza2muaI,96
13
+ pytbox/cli/categraf/commands.py,sha256=M-coJaHhb5I9fMW7OMWe9SMrs_RmSm4hSIJ1CPS0Ipc,1874
14
+ pytbox/cli/common/__init__.py,sha256=1_OE4q6OIUQkpwXBWqiKHr7SXxEu925hRgmd2hulIy4,70
15
+ pytbox/cli/common/options.py,sha256=Xv1wXja-fdaQVY6_FzvYRrXDozeTOJV6BL8zYIKJE9k,773
16
+ pytbox/cli/common/utils.py,sha256=0uQ5L1l5cySFheixB91B9ptq0p6tGsBhqvvm3ouRGbI,9050
17
+ pytbox/cli/formatters/__init__.py,sha256=4o85w4j-A-O1oBLvuE9q8AFiJ2C9rvB3MIKsy5VvdfQ,101
18
+ pytbox/cli/formatters/output.py,sha256=h5WhZlQk1rjmxEj88Jy5ODLcv6L5zfGUhks_3AWIkKU,5455
19
+ pytbox/common/__init__.py,sha256=3JWfgCQZKZuSH5NCE7OCzKwq82pkyop9l7sH5YSNyfU,122
20
+ pytbox/database/mongo.py,sha256=CSpHC7iR-M0BaVxXz5j6iXjMKPgXJX_G7MrjCj5Gm8Q,3478
21
+ pytbox/database/victoriametrics.py,sha256=PfeshtlgZNfbiq7Fo4ibJruWYeAKryBXu8ckl8YC1jM,4389
22
+ pytbox/feishu/client.py,sha256=kwGLseGT_iQUFmSqpuS2_77WmxtHstD64nXvktuQ3B4,5865
23
+ pytbox/feishu/endpoints.py,sha256=KmLB7yq0mpJgkWsTgGCufrejrPGV6JP0_p1GE6Pp4yI,40264
24
+ pytbox/feishu/errors.py,sha256=79qFAHZw7jDj3gnWAjI1-W4tB0q1_aSfdjee4xzXeuI,1179
25
+ pytbox/feishu/helpers.py,sha256=jhSkHiUw4822QBXx2Jw8AksogZdakZ-3QqvC3lB3qEI,201
26
+ pytbox/feishu/typing.py,sha256=3hWkJgOi-v2bt9viMxkyvNHsPgrbAa0aZOxsZYg2vdM,122
27
+ pytbox/log/logger.py,sha256=7ZisXRxLb_MVbIqlYHWoTbj1EA0Z4G5SZvITlt1IKW8,7416
28
+ pytbox/log/victorialog.py,sha256=gffEiq38adv9sC5oZeMcyKghd3SGfRuqtZOFuqHQF6E,4139
29
+ pytbox/utils/env.py,sha256=jO_-BKbGuDU7lIL9KYkcxGCzQwTXfxD4mIYtSAjREmI,622
30
+ pytbox/utils/load_config.py,sha256=wNCDPLH7xet5b9pUlTz6VsBejRsJZ7LP85wWMaITBYg,3042
31
+ pytbox/utils/ping_checker.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
32
+ pytbox/utils/response.py,sha256=kXjlwt0WVmLRam2eu1shzX2cQ7ux4cCQryaPGYwle5g,1247
33
+ pytbox/utils/richutils.py,sha256=OT9_q2Q1bthzB0g1GlhZVvM4ZAepJRKL6a_Vsr6vEqo,487
34
+ pytbox/utils/timeutils.py,sha256=XbK2KB-SVi7agNqoQN7i40wysrZvrGuwebViv1Cw-Ok,20226
35
+ pytbox-0.0.6.dist-info/METADATA,sha256=XTF_u4mj5KSUyGRNBtrFiA-UHffcS0gZwsYH62DPKl4,6245
36
+ pytbox-0.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ pytbox-0.0.6.dist-info/entry_points.txt,sha256=YaTOJ2oPjOiv2SZwY0UC-UA9QS2phRH1oMvxGnxO0Js,43
38
+ pytbox-0.0.6.dist-info/top_level.txt,sha256=YADgWue-Oe128ptN3J2hS3GB0Ncc5uZaSUM3e1rwswE,7
39
+ pytbox-0.0.6.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pytbox = pytbox.cli:main
pytbox/common/base.py DELETED
File without changes
@@ -1,27 +0,0 @@
1
- pytbox/base.py,sha256=nk65USdvAhltFJGCMXPmr02F9sTM7gcvlYuzL4sUT1k,1621
2
- pytbox/dida365.py,sha256=pUMPB9AyLZpTTbaz2LbtzdEpyjvuGf4YlRrCvM5sbJo,10545
3
- pytbox/onepassword_connect.py,sha256=nD3xTl1ykQ4ct_dCRRF138gXCtk-phPfKYXuOn-P7Z8,3064
4
- pytbox/onepassword_sa.py,sha256=08iUcYud3aEHuQcUsem9bWNxdXKgaxFbMy9yvtr-DZQ,6995
5
- pytbox/alert/alert_handler.py,sha256=arQkm95q2aqZomwflh_WWFwDw7aZyui-N5-FrxgRTZ8,5012
6
- pytbox/alert/ping.py,sha256=g36X0U3U8ndZqfpVIcuoxJJ0X5gST3I_IwjTQC1roHA,779
7
- pytbox/alicloud/sls.py,sha256=UR4GdI86dCKAFI2xt_1DELu7q743dpd3xrYtuNpfC5A,4065
8
- pytbox/common/__init__.py,sha256=3JWfgCQZKZuSH5NCE7OCzKwq82pkyop9l7sH5YSNyfU,122
9
- pytbox/common/base.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- pytbox/database/mongo.py,sha256=QyCeSoyWoJdIFXxjDmbV5t-xU7wN3x-Ig0I8pvRmBwg,1864
11
- pytbox/database/victoriametrics.py,sha256=fh6wghcWLhQxizVljC6X22gqXr2CioB1L4qixSi33Ms,4388
12
- pytbox/feishu/client.py,sha256=kwGLseGT_iQUFmSqpuS2_77WmxtHstD64nXvktuQ3B4,5865
13
- pytbox/feishu/endpoints.py,sha256=z_nHGXAjlIyYdFnbAWY4hfzzHrENJvoNOqY9sA6-FLk,40009
14
- pytbox/feishu/errors.py,sha256=79qFAHZw7jDj3gnWAjI1-W4tB0q1_aSfdjee4xzXeuI,1179
15
- pytbox/feishu/helpers.py,sha256=jhSkHiUw4822QBXx2Jw8AksogZdakZ-3QqvC3lB3qEI,201
16
- pytbox/feishu/typing.py,sha256=3hWkJgOi-v2bt9viMxkyvNHsPgrbAa0aZOxsZYg2vdM,122
17
- pytbox/log/logger.py,sha256=4IHmMg14Rwekmc7AeKGDbsDZryhcD6elaJZsvpdYJ80,5659
18
- pytbox/log/victorialog.py,sha256=50cVPxq9PzzzkzQHhgv_785K3GeF8B2mEnChdeekZWU,4137
19
- pytbox/utils/env.py,sha256=jO_-BKbGuDU7lIL9KYkcxGCzQwTXfxD4mIYtSAjREmI,622
20
- pytbox/utils/load_config.py,sha256=wNCDPLH7xet5b9pUlTz6VsBejRsJZ7LP85wWMaITBYg,3042
21
- pytbox/utils/ping_checker.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
22
- pytbox/utils/response.py,sha256=kXjlwt0WVmLRam2eu1shzX2cQ7ux4cCQryaPGYwle5g,1247
23
- pytbox/utils/timeutils.py,sha256=7ZI-TU2FCUV0Gyc_QUjhSreto8SVIs54e2CZgGix5pI,1628
24
- pytbox-0.0.4.dist-info/METADATA,sha256=bgImPZ1wSfkX-ARmOiiBDaRwrrdHqf9sgzcrs5uGUcw,6006
25
- pytbox-0.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- pytbox-0.0.4.dist-info/top_level.txt,sha256=YADgWue-Oe128ptN3J2hS3GB0Ncc5uZaSUM3e1rwswE,7
27
- pytbox-0.0.4.dist-info/RECORD,,
File without changes