pten 0.1.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.
- pten-0.1.0/PKG-INFO +15 -0
- pten-0.1.0/README.md +12 -0
- pten-0.1.0/setup.cfg +4 -0
- pten-0.1.0/setup.py +27 -0
- pten-0.1.0/src/pten/__init__.py +45 -0
- pten-0.1.0/src/pten/keys.py +205 -0
- pten-0.1.0/src/pten/notice.py +216 -0
- pten-0.1.0/src/pten/wwapi.py +710 -0
- pten-0.1.0/src/pten/wwcontact.py +316 -0
- pten-0.1.0/src/pten/wwcrypt.py +285 -0
- pten-0.1.0/src/pten/wwdoc.py +329 -0
- pten-0.1.0/src/pten/wwmessager.py +679 -0
- pten-0.1.0/src/pten.egg-info/PKG-INFO +15 -0
- pten-0.1.0/src/pten.egg-info/SOURCES.txt +15 -0
- pten-0.1.0/src/pten.egg-info/dependency_links.txt +1 -0
- pten-0.1.0/src/pten.egg-info/requires.txt +4 -0
- pten-0.1.0/src/pten.egg-info/top_level.txt +1 -0
pten-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 1.2
|
|
2
|
+
Name: pten
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A tool to use Wework API quickly and easily
|
|
5
|
+
Home-page: https://github.com/bendell02/pten
|
|
6
|
+
Author: PENGyong
|
|
7
|
+
Author-email: 1203029076@qq.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Description: UNKNOWN
|
|
10
|
+
Keywords: wework qywx wechat weixin robot app
|
|
11
|
+
Platform: UNKNOWN
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.8
|
pten-0.1.0/README.md
ADDED
pten-0.1.0/setup.cfg
ADDED
pten-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
requires = ["apscheduler", "lunardate", "openai", "pycryptodome"]
|
|
4
|
+
test_requirements = [
|
|
5
|
+
"pytest>=3",
|
|
6
|
+
"pytest-mock>=3",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
setup(
|
|
10
|
+
name="pten",
|
|
11
|
+
version="0.1.0",
|
|
12
|
+
description="A tool to use Wework API quickly and easily",
|
|
13
|
+
author="PENGyong",
|
|
14
|
+
author_email="1203029076@qq.com",
|
|
15
|
+
url="https://github.com/bendell02/pten",
|
|
16
|
+
packages=find_packages(where="src"),
|
|
17
|
+
package_dir={"": "src"},
|
|
18
|
+
python_requires=">=3.8",
|
|
19
|
+
install_requires=requires,
|
|
20
|
+
tests_require=test_requirements,
|
|
21
|
+
classifiers=[
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
],
|
|
26
|
+
keywords="wework qywx wechat weixin robot app",
|
|
27
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from logging.handlers import RotatingFileHandler
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
5
|
+
logger.setLevel(logging.INFO)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LogColors:
|
|
9
|
+
DEBUG = "\033[96m"
|
|
10
|
+
INFO = "\033[92m"
|
|
11
|
+
WARNING = "\033[93m"
|
|
12
|
+
ERROR = "\033[91m"
|
|
13
|
+
CRITICAL = "\033[41m\033[97m"
|
|
14
|
+
RESET = "\033[0m"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ColoredFormatter(logging.Formatter):
|
|
18
|
+
def format(self, record):
|
|
19
|
+
level_color = {
|
|
20
|
+
logging.DEBUG: LogColors.DEBUG,
|
|
21
|
+
logging.INFO: LogColors.INFO,
|
|
22
|
+
logging.WARNING: LogColors.WARNING,
|
|
23
|
+
logging.ERROR: LogColors.ERROR,
|
|
24
|
+
logging.CRITICAL: LogColors.CRITICAL,
|
|
25
|
+
}.get(record.levelno, LogColors.RESET)
|
|
26
|
+
|
|
27
|
+
message = super().format(record)
|
|
28
|
+
return f"{level_color}{message}{LogColors.RESET}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
formatter = ColoredFormatter(
|
|
32
|
+
"%(asctime)s | %(levelname)s | %(filename)s:%(lineno)d:%(funcName)s | %(message)s"
|
|
33
|
+
)
|
|
34
|
+
console_handler = logging.StreamHandler()
|
|
35
|
+
console_handler.setFormatter(formatter)
|
|
36
|
+
|
|
37
|
+
formatter = logging.Formatter(
|
|
38
|
+
"%(asctime)s | %(levelname)s | %(filename)s:%(lineno)d:%(funcName)s | %(message)s"
|
|
39
|
+
)
|
|
40
|
+
maxBytes = 30 * 1024 * 1024 # 30MB
|
|
41
|
+
file_handler = RotatingFileHandler(__name__ + ".log", maxBytes=maxBytes, backupCount=3)
|
|
42
|
+
file_handler.setFormatter(formatter)
|
|
43
|
+
|
|
44
|
+
logger.addHandler(file_handler)
|
|
45
|
+
logger.addHandler(console_handler)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pten.keys
|
|
3
|
+
~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
This module implements the keys class for getting keys from local file.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . import logger
|
|
10
|
+
import configparser
|
|
11
|
+
from configparser import ConfigParser
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
import json
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Keys:
|
|
18
|
+
"""Keys class for getting keys from local file
|
|
19
|
+
|
|
20
|
+
:param keys_filepath: The path of the keys file. Default is "pten_keys.ini"
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, keys_filepath="pten_keys.ini", *args, **kwargs):
|
|
24
|
+
self.key_cfg = ConfigParser()
|
|
25
|
+
self.keys_filepath = Path(keys_filepath)
|
|
26
|
+
self.TOKEN_PATH = Path("pten_token.json")
|
|
27
|
+
self.bot_weebhook_key = None
|
|
28
|
+
self.access_token = None
|
|
29
|
+
self.access_token_expire_time = float("-inf")
|
|
30
|
+
self.corp_jsapi_ticket = None
|
|
31
|
+
self.corp_jsapi_ticket_expire_time = float("-inf")
|
|
32
|
+
self.app_jsapi_ticket = None
|
|
33
|
+
self.app_jsapi_ticket_expire_time = float("-inf")
|
|
34
|
+
|
|
35
|
+
logger.info(f"keys_filepath : {self.keys_filepath}")
|
|
36
|
+
|
|
37
|
+
if not self.keys_filepath.is_file():
|
|
38
|
+
logger.error(f"Can not find file {self.keys_filepath}")
|
|
39
|
+
|
|
40
|
+
def _get_local_keys(self, section: str, options=[]):
|
|
41
|
+
"""Get keys from local file
|
|
42
|
+
:param section: Section name of the keys
|
|
43
|
+
:param options: The keys you want to get
|
|
44
|
+
:return: generator of the keys
|
|
45
|
+
"""
|
|
46
|
+
if self.keys_filepath.is_file():
|
|
47
|
+
self.key_cfg.clear()
|
|
48
|
+
self.key_cfg.read(self.keys_filepath)
|
|
49
|
+
try:
|
|
50
|
+
for option in options:
|
|
51
|
+
yield self.key_cfg.get(section, option)
|
|
52
|
+
except (configparser.NoSectionError, configparser.NoOptionError):
|
|
53
|
+
raise configparser.Error("KeyConfigError")
|
|
54
|
+
else:
|
|
55
|
+
raise FileNotFoundError(f"Can not find file {self.keys_filepath}")
|
|
56
|
+
|
|
57
|
+
def get_key(self, section: str, option: str):
|
|
58
|
+
"""Get keys from local file
|
|
59
|
+
:param section: Section name of the keys
|
|
60
|
+
:param option: The key you want to get
|
|
61
|
+
:return: key value
|
|
62
|
+
"""
|
|
63
|
+
return next(self._get_local_keys(section, [option]))
|
|
64
|
+
|
|
65
|
+
def get_keys(self, section: str, options=[]):
|
|
66
|
+
"""Get keys from local file
|
|
67
|
+
:param section: Section name of the keys
|
|
68
|
+
:param options: The keys you want to get
|
|
69
|
+
:return: dict of the keys
|
|
70
|
+
"""
|
|
71
|
+
res = {}
|
|
72
|
+
for k, v in zip(options, self._get_local_keys(section, options)):
|
|
73
|
+
res.update({k: v})
|
|
74
|
+
return res
|
|
75
|
+
|
|
76
|
+
def get_debug_mode(self):
|
|
77
|
+
debug_mode_str = "false"
|
|
78
|
+
try:
|
|
79
|
+
debug_mode_str = self.get_key("globals", "debug_mode")
|
|
80
|
+
except (configparser.Error, FileNotFoundError):
|
|
81
|
+
debug_mode_str = "false"
|
|
82
|
+
|
|
83
|
+
debug_mode = debug_mode_str.lower() in ("true", "yes", "on", "1")
|
|
84
|
+
return debug_mode
|
|
85
|
+
|
|
86
|
+
def get_proxies(self):
|
|
87
|
+
proxies = None
|
|
88
|
+
try:
|
|
89
|
+
proxies = self.get_keys(section="proxies", options=["http", "https"])
|
|
90
|
+
except (configparser.Error, FileNotFoundError):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
return proxies
|
|
94
|
+
|
|
95
|
+
def get_bot_weebhook_key(self):
|
|
96
|
+
if self.bot_weebhook_key is None:
|
|
97
|
+
key = next(self._get_local_keys(section="bot", options=["webhook_key"]))
|
|
98
|
+
self.bot_weebhook_key = key
|
|
99
|
+
|
|
100
|
+
return self.bot_weebhook_key
|
|
101
|
+
|
|
102
|
+
def get_app_agentid(self):
|
|
103
|
+
return next(self._get_local_keys(section="app", options=["agentid"]))
|
|
104
|
+
|
|
105
|
+
def get_contact_sync_secret(self):
|
|
106
|
+
try:
|
|
107
|
+
s = self.get_key("wwapi", "contact_sync_secret")
|
|
108
|
+
except StopIteration:
|
|
109
|
+
logger.warning("Can not find contact_sync_secret in keys ini file")
|
|
110
|
+
s = None
|
|
111
|
+
return s
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def load_from_file(file_path: Path, key):
|
|
115
|
+
if not file_path.is_file():
|
|
116
|
+
raise FileNotFoundError(f"Can not find file {file_path}.")
|
|
117
|
+
|
|
118
|
+
dict = json.loads(file_path.read_text())
|
|
119
|
+
if key not in dict:
|
|
120
|
+
raise KeyError(f"Can not find token of {key}.")
|
|
121
|
+
|
|
122
|
+
return dict[key]
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def save_to_file(file_path: Path, key, info):
|
|
126
|
+
token_dict = {}
|
|
127
|
+
if file_path.is_file():
|
|
128
|
+
token_dict = json.loads(file_path.read_text())
|
|
129
|
+
|
|
130
|
+
token_dict.update({key: info})
|
|
131
|
+
file_path.write_text(json.dumps(token_dict))
|
|
132
|
+
|
|
133
|
+
def get_access_token(self, token_key):
|
|
134
|
+
if self.access_token_expire_time > datetime.now().timestamp():
|
|
135
|
+
return self.access_token
|
|
136
|
+
|
|
137
|
+
token_info = Keys.load_from_file(self.TOKEN_PATH, token_key)
|
|
138
|
+
self.access_token_expire_time = token_info.get("expire_time", float("-inf"))
|
|
139
|
+
if self.access_token_expire_time < datetime.now().timestamp():
|
|
140
|
+
logger.warning(f"Token of {token_key} is expired.")
|
|
141
|
+
raise Exception("Token expired")
|
|
142
|
+
|
|
143
|
+
self.access_token = token_info["access_token"]
|
|
144
|
+
return self.access_token
|
|
145
|
+
|
|
146
|
+
def save_access_token(self, token_key, access_token):
|
|
147
|
+
self.access_token = access_token
|
|
148
|
+
self.access_token_expire_time = datetime.now().timestamp() + 7200
|
|
149
|
+
token_info = {
|
|
150
|
+
"access_token": access_token,
|
|
151
|
+
"expire_time": self.access_token_expire_time,
|
|
152
|
+
}
|
|
153
|
+
Keys.save_to_file(self.TOKEN_PATH, token_key, token_info)
|
|
154
|
+
|
|
155
|
+
def get_corp_jsapi_ticket(self, token_key):
|
|
156
|
+
now = datetime.now().timestamp()
|
|
157
|
+
if self.corp_jsapi_ticket_expire_time > now:
|
|
158
|
+
return self.corp_jsapi_ticket
|
|
159
|
+
|
|
160
|
+
ticket_key = token_key + "_corp_jsapi_ticket"
|
|
161
|
+
ticket_info = Keys.load_from_file(self.TOKEN_PATH, ticket_key)
|
|
162
|
+
expire_time = ticket_info.get("expire_time", float("-inf"))
|
|
163
|
+
self.corp_jsapi_ticket_expire_time = expire_time
|
|
164
|
+
if expire_time < now:
|
|
165
|
+
logger.warning(f"Ticket of {ticket_key} is expired.")
|
|
166
|
+
raise Exception("Ticket expired")
|
|
167
|
+
|
|
168
|
+
self.corp_jsapi_ticket = ticket_info["corp_jsapi_ticket"]
|
|
169
|
+
return self.corp_jsapi_ticket
|
|
170
|
+
|
|
171
|
+
def save_corp_jsapi_ticket(self, token_key, corp_jsapi_ticket):
|
|
172
|
+
ticket_key = token_key + "_corp_jsapi_ticket"
|
|
173
|
+
self.corp_jsapi_ticket = corp_jsapi_ticket
|
|
174
|
+
self.corp_jsapi_ticket_expire_time = datetime.now().timestamp() + 7200
|
|
175
|
+
ticket_info = {
|
|
176
|
+
"corp_jsapi_ticket": corp_jsapi_ticket,
|
|
177
|
+
"expire_time": self.corp_jsapi_ticket_expire_time,
|
|
178
|
+
}
|
|
179
|
+
Keys.save_to_file(self.TOKEN_PATH, ticket_key, ticket_info)
|
|
180
|
+
|
|
181
|
+
def get_app_jsapi_ticket(self, token_key):
|
|
182
|
+
now = datetime.now().timestamp()
|
|
183
|
+
if self.app_jsapi_ticket_expire_time > now:
|
|
184
|
+
return self.app_jsapi_ticket
|
|
185
|
+
|
|
186
|
+
ticket_key = token_key + "_app_jsapi_ticket"
|
|
187
|
+
ticket_info = Keys.load_from_file(self.TOKEN_PATH, ticket_key)
|
|
188
|
+
expire_time = ticket_info.get("expire_time", float("-inf"))
|
|
189
|
+
self.app_jsapi_ticket_expire_time = expire_time
|
|
190
|
+
if expire_time < now:
|
|
191
|
+
logger.warning(f"Ticket of {ticket_key} is expired.")
|
|
192
|
+
raise Exception("Ticket expired")
|
|
193
|
+
|
|
194
|
+
self.app_jsapi_ticket = ticket_info["app_jsapi_ticket"]
|
|
195
|
+
return self.app_jsapi_ticket
|
|
196
|
+
|
|
197
|
+
def save_app_jsapi_ticket(self, token_key, app_jsapi_ticket):
|
|
198
|
+
ticket_key = token_key + "_app_jsapi_ticket"
|
|
199
|
+
self.app_jsapi_ticket = app_jsapi_ticket
|
|
200
|
+
self.app_jsapi_ticket_expire_time = datetime.now().timestamp() + 7200
|
|
201
|
+
ticket_info = {
|
|
202
|
+
"app_jsapi_ticket": app_jsapi_ticket,
|
|
203
|
+
"expire_time": self.app_jsapi_ticket_expire_time,
|
|
204
|
+
}
|
|
205
|
+
Keys.save_to_file(self.TOKEN_PATH, ticket_key, ticket_info)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pten.notice
|
|
3
|
+
~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
This module implements the notice functions.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . import logger
|
|
10
|
+
from .keys import Keys
|
|
11
|
+
from apscheduler.schedulers.blocking import BaseScheduler
|
|
12
|
+
import datetime
|
|
13
|
+
from lunardate import LunarDate
|
|
14
|
+
from openai import OpenAI
|
|
15
|
+
import requests
|
|
16
|
+
import json
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Notice:
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.report_func = print
|
|
22
|
+
self.scheduler: BaseScheduler = None
|
|
23
|
+
|
|
24
|
+
def set_report_func(self, func):
|
|
25
|
+
self.report_func = func
|
|
26
|
+
|
|
27
|
+
def set_scheduler(self, scheduler: BaseScheduler):
|
|
28
|
+
self.scheduler = scheduler
|
|
29
|
+
|
|
30
|
+
def report_text(self, text):
|
|
31
|
+
self.report_func(text)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Birthday(Notice):
|
|
35
|
+
def __init__(self):
|
|
36
|
+
super().__init__()
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def get_date_str_from_lunar_date(
|
|
40
|
+
lunar_year, lunar_month, lunar_day, hour=8, minute=3, is_leap_month=False
|
|
41
|
+
):
|
|
42
|
+
lunar_date = LunarDate(lunar_year, lunar_month, lunar_day, is_leap_month)
|
|
43
|
+
solar_date = lunar_date.toSolarDate()
|
|
44
|
+
return f"{solar_date} {hour:02d}:{minute:02d}:00"
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def is_leap_month(lunar_year, lunar_month):
|
|
48
|
+
return lunar_month == LunarDate.leapMonthForYear(lunar_year)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def get_now_lunar_date():
|
|
52
|
+
now_solar_date = datetime.datetime.now()
|
|
53
|
+
year = now_solar_date.year
|
|
54
|
+
month = now_solar_date.month
|
|
55
|
+
day = now_solar_date.day
|
|
56
|
+
now_lunar_date = LunarDate.fromSolarDate(year, month, day)
|
|
57
|
+
return now_lunar_date
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def generate_birthday_greeting(who, greeting_words=None):
|
|
61
|
+
msg = f"今天是{who}的生日,让我们来祝福{who}吧"
|
|
62
|
+
if greeting_words:
|
|
63
|
+
msg = f"今天是{who}的生日,{greeting_words}"
|
|
64
|
+
|
|
65
|
+
return msg
|
|
66
|
+
|
|
67
|
+
def _add_lunar_schedule(
|
|
68
|
+
self, msg, lunar_year, lunar_month, lunar_day, hour, minute
|
|
69
|
+
):
|
|
70
|
+
run_date = self.get_date_str_from_lunar_date(
|
|
71
|
+
lunar_year, lunar_month, lunar_day, hour, minute, False
|
|
72
|
+
)
|
|
73
|
+
args = [msg, lunar_month, lunar_day, hour, minute]
|
|
74
|
+
func = self.report_lunar_birthday
|
|
75
|
+
self.scheduler.add_job(func, "date", run_date=run_date, args=args)
|
|
76
|
+
|
|
77
|
+
# solve leap month
|
|
78
|
+
if not self.is_leap_month(lunar_year, lunar_month):
|
|
79
|
+
return
|
|
80
|
+
run_date = self.get_date_str_from_lunar_date(
|
|
81
|
+
lunar_year, lunar_month, lunar_day, hour, minute, True
|
|
82
|
+
)
|
|
83
|
+
self.scheduler.add_job(func, "date", run_date=run_date, args=args)
|
|
84
|
+
|
|
85
|
+
def _add_solar_schedule(self, msg, year, month, day, hour, minute):
|
|
86
|
+
solar_date = datetime.date(year, month, day)
|
|
87
|
+
run_date = f"{solar_date} {hour:02d}:{minute:02d}:00"
|
|
88
|
+
args = [msg, month, day, hour, minute]
|
|
89
|
+
func = self.report_solar_birthday
|
|
90
|
+
self.scheduler.add_job(func, "date", run_date=run_date, args=args)
|
|
91
|
+
|
|
92
|
+
def report_lunar_birthday(self, msg, lunar_month, lunar_day, hour=8, minute=3):
|
|
93
|
+
self.report_func(msg)
|
|
94
|
+
|
|
95
|
+
if self.scheduler is None:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# add next year's schedule job
|
|
99
|
+
now_lunar_date = self.get_now_lunar_date()
|
|
100
|
+
self._add_lunar_schedule(
|
|
101
|
+
msg, now_lunar_date.year + 1, lunar_month, lunar_day, hour, minute
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def report_solar_birthday(self, msg, month, day, hour=8, minute=3):
|
|
105
|
+
self.report_func(msg)
|
|
106
|
+
|
|
107
|
+
if self.scheduler is None:
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
# add next year's schedule job
|
|
111
|
+
today = datetime.date.today()
|
|
112
|
+
next_year = today.year + 1
|
|
113
|
+
self._add_solar_schedule(msg, next_year, month, day, hour, minute)
|
|
114
|
+
|
|
115
|
+
def add_lunar_schedule(
|
|
116
|
+
self,
|
|
117
|
+
lunar_month,
|
|
118
|
+
lunar_day,
|
|
119
|
+
hour=8,
|
|
120
|
+
minute=3,
|
|
121
|
+
who="someone",
|
|
122
|
+
greeting_words=None,
|
|
123
|
+
):
|
|
124
|
+
if self.scheduler is None:
|
|
125
|
+
logger.error("Scheduler is not set. Please set_scheduler() first.")
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
msg = self.generate_birthday_greeting(who, greeting_words)
|
|
129
|
+
|
|
130
|
+
now_lunar_date = self.get_now_lunar_date()
|
|
131
|
+
lunar_date = LunarDate(now_lunar_date.year, lunar_month, lunar_day)
|
|
132
|
+
if lunar_date < LunarDate.today():
|
|
133
|
+
lunar_date = LunarDate(now_lunar_date.year + 1, lunar_month, lunar_day)
|
|
134
|
+
|
|
135
|
+
self._add_lunar_schedule(
|
|
136
|
+
msg, lunar_date.year, lunar_month, lunar_day, hour, minute
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def add_solar_schedule(
|
|
140
|
+
self, month, day, hour=8, minute=3, who="someone", greeting_words=None
|
|
141
|
+
):
|
|
142
|
+
if self.scheduler is None:
|
|
143
|
+
logger.error("Scheduler is not set. Please set_scheduler() first.")
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
msg = self.generate_birthday_greeting(who, greeting_words)
|
|
147
|
+
|
|
148
|
+
now = datetime.datetime.now()
|
|
149
|
+
solar_date = datetime.date(now.year, month, day)
|
|
150
|
+
if solar_date < now.date():
|
|
151
|
+
solar_date = solar_date.replace(year=now.year + 1)
|
|
152
|
+
|
|
153
|
+
self._add_solar_schedule(msg, solar_date.year, month, day, hour, minute)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class Deepseek(Notice):
|
|
157
|
+
def __init__(self, keys_filepath="pten_keys.ini", **kwargs):
|
|
158
|
+
super().__init__()
|
|
159
|
+
self.keys = Keys(keys_filepath)
|
|
160
|
+
sk_api_key = self.keys.get_key("ai", "deepseek_api_key")
|
|
161
|
+
self.deepseek_client = OpenAI(
|
|
162
|
+
api_key=sk_api_key,
|
|
163
|
+
base_url="https://api.deepseek.com",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def get_completion(self, prompt):
|
|
167
|
+
response = self.deepseek_client.chat.completions.create(
|
|
168
|
+
model="deepseek-chat",
|
|
169
|
+
messages=[
|
|
170
|
+
{"role": "system", "content": "You are a helpful assistant"},
|
|
171
|
+
{"role": "user", "content": prompt},
|
|
172
|
+
],
|
|
173
|
+
stream=False,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return response.choices[0].message.content
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class Weather(Notice):
|
|
180
|
+
def __init__(self, keys_filepath="pten_keys.ini", **kwargs):
|
|
181
|
+
super().__init__()
|
|
182
|
+
self.keys = Keys(keys_filepath)
|
|
183
|
+
self.api_key = self.keys.get_key("weather", "seniverse_api_key")
|
|
184
|
+
self.cities = {}
|
|
185
|
+
|
|
186
|
+
def add_city(self, city_name, city_code):
|
|
187
|
+
self.cities[city_name] = city_code
|
|
188
|
+
|
|
189
|
+
def get_weather(self, city_code):
|
|
190
|
+
url = f"https://api.seniverse.com/v3/weather/now.json?key={self.api_key}&location={city_code}&language=zh-Hans&unit=c"
|
|
191
|
+
|
|
192
|
+
response = requests.get(url)
|
|
193
|
+
weather_json = json.loads(response.text)
|
|
194
|
+
weather = weather_json["results"][0]["now"]
|
|
195
|
+
|
|
196
|
+
return weather
|
|
197
|
+
|
|
198
|
+
def get_city_weather(self, city_name, city_code):
|
|
199
|
+
weather = self.get_weather(city_code)
|
|
200
|
+
|
|
201
|
+
weather_text = weather.get("text")
|
|
202
|
+
weather_temperature = weather.get("temperature")
|
|
203
|
+
|
|
204
|
+
weather_str = f"{city_name}: {weather_text},\t 温度: {weather_temperature}度"
|
|
205
|
+
|
|
206
|
+
return weather_str
|
|
207
|
+
|
|
208
|
+
def report_weather(self):
|
|
209
|
+
if not self.cities:
|
|
210
|
+
logger.warning("No city added. Please add_city() first.")
|
|
211
|
+
return
|
|
212
|
+
weather_str = "今日天气:"
|
|
213
|
+
for city_name, city_code in self.cities.items():
|
|
214
|
+
weather_str += "\n" + self.get_city_weather(city_name, city_code)
|
|
215
|
+
|
|
216
|
+
self.report_func(weather_str)
|