Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] 重构消息发送模块,简化消息发送函数 #52

Merged
merged 6 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ config.ini
data/

logs/

.sqllsrc.json
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ docker run -d \
--name wxBotWebhook \
-p 3001:3001 \
-e LOGIN_API_TOKEN="<Token>" \
-e RECVD_MSG_API="http://<内网IP>:<接收消息端口>/receive_msg" \
-e RECVD_MSG_API="http(s)://<宿主机IP>:<接收消息端口>/receive_msg" \
dannicool/docker-wechatbot-webhook
```

- `<Token>`:登录令牌(不是密码),自己设置一个好记的。
- `<内网IP>`:填入服务器的**内网IP**。如果是在自己电脑,则填入 `127.0.0.1`
- `<接收消息端口>`:设置一个接收消息的端口,此项目中默认为 `4000`。
- `<Token>`:登录令牌(可选)
- `<宿主机IP>`:填入 Docker 的宿主机地址
- `<接收消息端口>`:设置一个接收消息的端口,默认为 `4000`。

3. 登录微信

Expand All @@ -45,7 +45,7 @@ dannicool/docker-wechatbot-webhook
docker logs -f wxBotWebhook
```

### 启动服务器
### 启动 WeChatter

1. 下载源代码

Expand Down
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def main():
sqlite_manager = SqliteManager("data/wechatter.sqlite")
sqlite_manager.excute_folder("wechatter/sqlite/sqls")

logger.info("Wechatter 启动成功!")
logger.info("WeChatter 启动成功!")
# 启动uvicorn
uvicorn.run(app, host="0.0.0.0", port=config.wechatter_port)

Expand Down
40 changes: 22 additions & 18 deletions tests/commands/test_food_calories/test_food_calories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,58 @@

class TestFoodCaloriesCommand(unittest.TestCase):
def setUp(self):
with open('tests/commands/test_food_calories/food_calories_response.html.test') as f:
with open(
"tests/commands/test_food_calories/food_calories_response.html.test"
) as f:
r_html = f.read()
self.food_calories_response = Response()
self.food_calories_response._content = r_html.encode('utf-8')
self.food_calories_response._content = r_html.encode("utf-8")
with open("tests/commands/test_food_calories/one_food_response.html.test") as f:
one_food_r_html = f.read()
self.one_food_response = Response()
self.one_food_response._content = one_food_r_html.encode('utf-8')
with open('tests/commands/test_food_calories/food_href_list.json') as f:
self.one_food_response._content = one_food_r_html.encode("utf-8")
with open("tests/commands/test_food_calories/food_href_list.json") as f:
self.food_href_list = json.load(f)
with open("tests/commands/test_food_calories/food_detail_list.json") as f:
self.food_detail_list = json.load(f)
with open("tests/commands/test_food_calories/one_food_detail.json") as f:
self.one_food_detail = json.load(f)

def test_parse_food_href_list_response_success(self):
result = food_calories.parse_food_href_list_response(self.food_calories_response)
result = food_calories._parse_food_href_list_response(
self.food_calories_response
)
self.assertEqual(result, self.food_href_list)

def test_parse_food_href_list_response_failure(self):
with self.assertRaises(Bs4ParsingError):
food_calories.parse_food_href_list_response(Response())

def test_get_food_detail_list_success(self):
result = food_calories.get_food_detail_list(self.food_href_list)
self.assertEqual(result, self.food_detail_list)
food_calories._parse_food_href_list_response(Response())

def test_get_food_detail_list_failure(self):
with self.assertRaises(Exception):
food_calories.get_food_detail_list([])
food_calories._get_food_detail_list([])

def test_parse_food_detail_response_success(self):
result = food_calories.parse_food_detail_response(self.one_food_response, "牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸")
result = food_calories._parse_food_detail_response(
self.one_food_response, "牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸"
)
self.assertEqual(result, self.one_food_detail)

def test_parse_food_detail_response_failure(self):
with self.assertRaises(Bs4ParsingError):
food_calories.parse_food_detail_response(Response(), "牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸")
food_calories._parse_food_detail_response(
Response(), "牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸"
)

def test_generate_food_message_success(self):
result = food_calories.generate_food_message(self.food_detail_list)
true_result = "✨=====食物列表=====✨\n1. 牛肉(肥瘦),又叫肥牛\n 🍲热量(大卡): 125.00\n 🍞碳水(克): 2.00\n 🥓脂肪(克): 4.20\n 🍗蛋白质(克): 19.90\n \U0001f966纤维素(克): 一\n2. 牛肉(精瘦),又叫牛肉,瘦牛肉\n 🍲热量(大卡): 113.00\n 🍞碳水(克): 1.30\n 🥓脂肪(克): 2.50\n 🍗蛋白质(克): 21.30\n \U0001f966纤维素(克): 0.00\n3. 牛腩,又叫牛肉(牛腩)、牛腩\n 🍲热量(大卡): 332.00\n 🍞碳水(克): 0.00\n 🥓脂肪(克): 29.30\n 🍗蛋白质(克): 17.10\n \U0001f966纤维素(克): 0.00\n4. 肥牛卷,又叫肥牛、火锅牛肉卷、牛肉卷\n 🍲热量(大卡): 250.00\n 🍞碳水(克): 0.02\n 🥓脂肪(克): 18.73\n 🍗蛋白质(克): 19.06\n \U0001f966纤维素(克): 0.00\n5. 牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸\n 🍲热量(大卡): 115.00\n 🍞碳水(克): 9.00\n 🥓脂肪(克): 3.70\n 🍗蛋白质(克): 11.20\n \U0001f966纤维素(克): 0.00\n🔵====含量(100克)====🔵' not found in '✨=====食物列表=====✨\n1. 牛肉(肥瘦),又叫肥牛\n🍲热量(大卡): 125.00\n🍞碳水(克): 2.00\n🥓脂肪(克): 4.20\n🍗蛋白质(克): 19.90\n\U0001f966纤维素(克): 一\n2. 牛肉(精瘦),又叫牛肉,瘦牛肉\n🍲热量(大卡): 113.00\n🍞碳水(克): 1.30\n🥓脂肪(克): 2.50\n🍗蛋白质(克): 21.30\n\U0001f966纤维素(克): 0.00\n3. 牛腩,又叫牛肉(牛腩)、牛腩\n🍲热量(大卡): 332.00\n🍞碳水(克): 0.00\n🥓脂肪(克): 29.30\n🍗蛋白质(克): 17.10\n\U0001f966纤维素(克): 0.00\n4. 肥牛卷,又叫肥牛、火锅牛肉卷、牛肉卷\n🍲热量(大卡): 250.00\n🍞碳水(克): 0.02\n🥓脂肪(克): 18.73\n🍗蛋白质(克): 19.06\n\U0001f966纤维素(克): 0.00\n5. 牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸\n🍲热量(大卡): 115.00\n🍞碳水(克): 9.00\n🥓脂肪(克): 3.70\n🍗蛋白质(克): 11.20\n\U0001f966纤维素(克): 0.00\n🔵====含量(100克)====🔵"
self.assertIn(result, true_result)
result = food_calories._generate_food_message(self.food_detail_list)
true_result = "✨=====食物列表=====✨\n1. 牛肉(肥瘦),又叫肥牛\n 🍲热量(大卡): 125.00\n 🍞碳水(克): 2.00\n 🥓脂肪(克): 4.20\n 🍗蛋白质(克): 19.90\n 🥦纤维素(克): 一\n2. 牛肉(精瘦),又叫牛肉,瘦牛肉\n 🍲热量(大卡): 113.00\n 🍞碳水(克): 1.30\n 🥓脂肪(克): 2.50\n 🍗蛋白质(克): 21.30\n 🥦纤维素(克): 0.00\n3. 牛腩,又叫牛肉(牛腩)、牛腩\n 🍲热量(大卡): 332.00\n 🍞碳水(克): 0.00\n 🥓脂肪(克): 29.30\n 🍗蛋白质(克): 17.10\n 🥦纤维素(克): 0.00\n4. 肥牛卷,又叫肥牛、火锅牛肉卷、牛肉卷\n 🍲热量(大卡): 250.00\n 🍞碳水(克): 0.02\n 🥓脂肪(克): 18.73\n 🍗蛋白质(克): 19.06\n 🥦纤维素(克): 0.00\n5. 牛肉丸,又叫火锅牛肉丸子,火锅牛肉丸\n 🍲热量(大卡): 115.00\n 🍞碳水(克): 9.00\n 🥓脂肪(克): 3.70\n 🍗蛋白质(克): 11.20\n 🥦纤维素(克): 0.00\n🔵====含量(100克)====🔵"
self.assertEqual(result, true_result)

def test_generate_food_message_failure(self):
with self.assertRaises(Exception):
food_calories.generate_food_message([])
food_calories._generate_food_message([])

def test_get_url_encoding_success(self):
result = food_calories.get_url_encoding("牛肉丸")
result = food_calories._get_url_encoding("牛肉丸")
self.assertEqual(result, "%E7%89%9B%E8%82%89%E4%B8%B8")
26 changes: 10 additions & 16 deletions tests/commands/test_gasoline_price/test_gasoline_price.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,33 @@


class TestGasolinePriceCommand(unittest.TestCase):

def setUp(self):
with open("tests/commands/test_gasoline_price/gasoline_price_response_html.test") as f:
with open(
"tests/commands/test_gasoline_price/gasoline_price_response_html.test"
) as f:
r_html = f.read()
self.response = Response()
self.response._content = r_html.encode('utf-8')
self.response._content = r_html.encode("utf-8")
with open("tests/commands/test_gasoline_price/gasoline_price_data") as f:
self.gasoline_price = f.read()

def test_get_gasoline_price_str_success(self):
result = gasoline_price.get_gasoline_price_str('广州')
self.assertIn('✨广州石化92汽油指导价✨', result)

def test_get_gasoline_price_str_failure(self):
with self.assertRaises(KeyError):
gasoline_price.get_gasoline_price_str('广州市')
gasoline_price.get_gasoline_price_str("广州市")

def test_parse_gasoline_price_response_success(self):
result = gasoline_price._parse_gasoline_price_response(self.response)
self.assertIn('广州中国石化92号汽油最新指导价格为', result)
true_result = "2024年02月04日,广州中国石化92号汽油最新指导价格为:7.84元每升,调整时间为2024-02-01,相对上次调整时间2024-01-18的油价涨了0.16元,涨幅达2.08%"
self.assertEqual(result, true_result)

def test_parse_gasoline_price_response_failure(self):
with self.assertRaises(Bs4ParsingError):
gasoline_price._parse_gasoline_price_response(Response())

def test_get_city_id_success(self):
result = gasoline_price._get_city_id('广州')
self.assertEqual(result, '440100')
result = gasoline_price._get_city_id("广州")
self.assertEqual(result, "440100")

def test_get_city_id_failure(self):
with self.assertRaises(KeyError):
gasoline_price._get_city_id('广州市')

def test_generate_gasoline_price_message_success(self):
result = gasoline_price._generate_gasoline_price_message(self.gasoline_price, '广州')
self.assertIn('✨广州石化92汽油指导价✨', result)
gasoline_price._get_city_id("广州市")
11 changes: 8 additions & 3 deletions wechatter/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import wechatter.app.routers as routers
import wechatter.config as config
from wechatter.config.parsers import parse_weather_cron_rule_list, parse_gasoline_price_cron_rule_list
from wechatter.config.parsers import (
parse_gasoline_price_cron_rule_list,
parse_weather_cron_rule_list,
)
from wechatter.scheduler import Scheduler

app = FastAPI()
Expand All @@ -12,12 +15,15 @@
if config.github_webhook_enabled:
app.include_router(routers.github_router)

# 定时任务
if config.weather_cron_enabled:
cron_tasks = parse_weather_cron_rule_list(config.weather_cron_rule_list)
Scheduler.add_cron_tasks(cron_tasks)

if config.gasoline_price_cron_enable:
cron_tasks = parse_gasoline_price_cron_rule_list(config.gasoline_price_cron_rule_list)
cron_tasks = parse_gasoline_price_cron_rule_list(
config.gasoline_price_cron_rule_list
)
Scheduler.add_cron_tasks(cron_tasks)

if not Scheduler.is_cron_tasks_empty():
Expand All @@ -28,7 +34,6 @@
async def startup_event():
scheduler.startup()


@app.on_event("shutdown")
async def shutdown_event():
scheduler.shutdown()
2 changes: 1 addition & 1 deletion wechatter/app/routers/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async def recv_github_webhook(request: Request):
try:
handler(data)
except ValueError as e:
logger.error(f"GitHub Webhook 处理失败: {e}")
logger.error(f"GitHub Webhook 处理失败: {str(e)}")
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content={"detail": str(e)}
)
Expand Down
6 changes: 3 additions & 3 deletions wechatter/app/routers/wechat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from wechatter.message import MessageHandler
from wechatter.message_forwarder import MessageForwarder
from wechatter.models.message import Message
from wechatter.sender import notify_logged_in, notify_logged_out
from wechatter.sender import notifier
from wechatter.sqlite.sqlite_manager import SqliteManager

router = APIRouter()
Expand Down Expand Up @@ -79,10 +79,10 @@ def handle_system_event(content: str) -> None:
# 判断是否为机器人登录消息
if content_dict["event"] == "login":
print("机器人登录成功")
notify_logged_in()
notifier.notify_logged_in()
elif content_dict["event"] == "logout":
print("机器人已退出登录")
notify_logged_out()
notifier.notify_logged_out()
elif content_dict["event"] == "error":
pass
else:
Expand Down
8 changes: 4 additions & 4 deletions wechatter/commands/_commands/bili_hot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from loguru import logger

from wechatter.commands.handlers import command
from wechatter.models.message import SendMessage, SendMessageType, SendTo
from wechatter.sender import Sender
from wechatter.models.message import SendTo
from wechatter.sender import sender
from wechatter.utils import get_request_json


Expand All @@ -19,9 +19,9 @@ def bili_hot_command_handler(to: SendTo, message: str = "") -> None:
except Exception as e:
error_message = f"获取Bilibili热搜失败,错误信息: {str(e)}"
logger.error(error_message)
Sender.send_msg(to, SendMessage(SendMessageType.TEXT, error_message))
sender.send_msg(to, error_message)
else:
Sender.send_msg(to, SendMessage(SendMessageType.TEXT, result))
sender.send_msg(to, result)


def get_bili_hot_str() -> str:
Expand Down
39 changes: 17 additions & 22 deletions wechatter/commands/_commands/copilot_gpt4.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import wechatter.config as config
import wechatter.utils.path_manager as pm
from wechatter.commands.handlers import command
from wechatter.models.message import SendMessage, SendMessageType, SendTo
from wechatter.sender import Sender
from wechatter.models.message import SendTo
from wechatter.sender import sender
from wechatter.sqlite.sqlite_manager import SqliteManager
from wechatter.utils import post_request_json
from wechatter.utils.time import get_current_timestamp
Expand Down Expand Up @@ -94,45 +94,40 @@ def gpt4_remove_command_handler(to: SendTo, message: str = "") -> None:
pass


def _send_text_msg(to: SendTo, message: str = "") -> None:
"""封装发送文本消息"""
Sender.send_msg(to, SendMessage(SendMessageType.TEXT, message))


def _gptx(model: str, to: SendTo, message: str = "") -> None:
wx_id = to.p_id
# 获取文件夹下最新的对话记录
chat_info = CopilotGPT4.get_chating_chat_info(wx_id, model)
if message == "": # /gpt4
# 判断对话是否有效
_send_text_msg(to, "正在创建新对话...")
sender.send_msg(to, "正在创建新对话...")
if chat_info is None or CopilotGPT4.is_chat_valid(chat_info):
CopilotGPT4.create_chat(wx_id=wx_id, model=model)
logger.info("创建新对话成功")
_send_text_msg(to, "创建新对话成功")
sender.send_msg(to, "创建新对话成功")
return
logger.info("对话未开始,继续上一次对话")
_send_text_msg(to, "对话未开始,继续上一次对话")
sender.send_msg(to, "对话未开始,继续上一次对话")
else: # /gpt4 <message>
# 如果没有对话记录,则创建新对话
_send_text_msg(to, f"正在调用 {model} 进行对话...")
sender.send_msg(to, f"正在调用 {model} 进行对话...")
if chat_info is None:
chat_info = CopilotGPT4.create_chat(wx_id=wx_id, model=model)
logger.info("无历史对话记录,创建新对话成功")
_send_text_msg(to, "无历史对话记录,创建新对话成功")
sender.send_msg(to, "无历史对话记录,创建新对话成功")
try:
response = CopilotGPT4.chat(chat_info, message)
logger.info(response)
_send_text_msg(to, response)
sender.send_msg(to, response)
except Exception as e:
error_message = f"调用Copilot-GPT4-Server失败,错误信息:{e}"
error_message = f"调用Copilot-GPT4-Server失败,错误信息:{str(e)}"
logger.error(error_message)
_send_text_msg(to, error_message)
sender.send_msg(to, error_message)


def _gptx_chats(model: str, to: SendTo, message: str = "") -> None:
response = CopilotGPT4.get_chat_list_str(to.p_id, model)
_send_text_msg(to, response)
sender.send_msg(to, response)


def _gptx_record(model: str, to: SendTo, message: str = "") -> None:
Expand All @@ -146,34 +141,34 @@ def _gptx_record(model: str, to: SendTo, message: str = "") -> None:
chat_info = CopilotGPT4.get_chat_info(wx_id, model, int(message))
if chat_info is None:
logger.waring("对话不存在")
_send_text_msg(to, "对话不存在")
sender.send_msg(to, "对话不存在")
return
response = CopilotGPT4.get_brief_conversation_str(chat_info)
logger.info(response)
_send_text_msg(to, response)
sender.send_msg(to, response)


def _gptx_continue(model: str, to: SendTo, message: str = "") -> None:
wx_id = to.p_id
# 判断message是否为数字
if not message.isdigit():
logger.info("请输入对话记录编号")
_send_text_msg(to, "请输入对话记录编号")
sender.send_msg(to, "请输入对话记录编号")
return
_send_text_msg(to, f"正在切换到对话记录 {message}...")
sender.send_msg(to, f"正在切换到对话记录 {message}...")
chat_info = CopilotGPT4.continue_chat(
wx_id=wx_id, model=model, chat_index=int(message)
)
if chat_info is None:
waring_message = "选择历史对话失败,对话不存在"
logger.waring(waring_message)
_send_text_msg(to, waring_message)
sender.send_msg(to, waring_message)
return
response = CopilotGPT4.get_brief_conversation_str(chat_info)
response += "====================\n"
response += "对话已选中,输入命令继续对话"
logger.info(response)
_send_text_msg(to, response)
sender.send_msg(to, response)


class ChatInfo:
Expand Down
8 changes: 4 additions & 4 deletions wechatter/commands/_commands/douyin_hot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from loguru import logger

from wechatter.commands.handlers import command
from wechatter.models.message import SendMessage, SendMessageType, SendTo
from wechatter.sender import Sender
from wechatter.models.message import SendTo
from wechatter.sender import sender
from wechatter.utils import get_request_json


Expand All @@ -19,9 +19,9 @@ def douyin_hot_command_handler(to: SendTo, message: str = "") -> None:
except Exception as e:
error_message = f"获取抖音热搜失败,错误信息: {str(e)}"
logger.error(error_message)
Sender.send_msg(to, SendMessage(SendMessageType.TEXT, error_message))
sender.send_msg(to, error_message)
else:
Sender.send_msg(to, SendMessage(SendMessageType.TEXT, result))
sender.send_msg(to, result)


def get_douyin_hot_str() -> str:
Expand Down
Loading
Loading