加入一个全局Client ,方便所有地方调用。
This commit is contained in:
@@ -1,106 +0,0 @@
|
|||||||
#! /usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
from openai import APIConnectionError, APIError, AuthenticationError, OpenAI
|
|
||||||
|
|
||||||
|
|
||||||
class ChatGPT():
|
|
||||||
def __init__(self, conf: dict) -> None:
|
|
||||||
key = conf.get("key")
|
|
||||||
api = conf.get("api")
|
|
||||||
proxy = conf.get("proxy")
|
|
||||||
prompt = conf.get("prompt")
|
|
||||||
self.model = conf.get("model", "gpt-3.5-turbo")
|
|
||||||
self.LOG = logging.getLogger("ChatGPT")
|
|
||||||
if proxy:
|
|
||||||
self.client = OpenAI(api_key=key, base_url=api, http_client=httpx.Client(proxy=proxy))
|
|
||||||
else:
|
|
||||||
self.client = OpenAI(api_key=key, base_url=api)
|
|
||||||
self.conversation_list = {}
|
|
||||||
self.system_content_msg = {"role": "system", "content": prompt}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'ChatGPT'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def value_check(conf: dict) -> bool:
|
|
||||||
if conf:
|
|
||||||
if conf.get("key") and conf.get("api") and conf.get("prompt"):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_answer(self, question: str, wxid: str) -> str:
|
|
||||||
# wxid或者roomid,个人时为微信id,群消息时为群id
|
|
||||||
self.updateMessage(wxid, question, "user")
|
|
||||||
rsp = ""
|
|
||||||
try:
|
|
||||||
ret = self.client.chat.completions.create(model=self.model,
|
|
||||||
messages=self.conversation_list[wxid],
|
|
||||||
temperature=0.2)
|
|
||||||
rsp = ret.choices[0].message.content
|
|
||||||
rsp = rsp[2:] if rsp.startswith("\n\n") else rsp
|
|
||||||
rsp = rsp.replace("\n\n", "\n")
|
|
||||||
self.updateMessage(wxid, rsp, "assistant")
|
|
||||||
except AuthenticationError:
|
|
||||||
self.LOG.error("OpenAI API 认证失败,请检查 API 密钥是否正确")
|
|
||||||
except APIConnectionError:
|
|
||||||
self.LOG.error("无法连接到 OpenAI API,请检查网络连接")
|
|
||||||
except APIError as e1:
|
|
||||||
self.LOG.error(f"OpenAI API 返回了错误:{str(e1)}")
|
|
||||||
except Exception as e0:
|
|
||||||
self.LOG.error(f"发生未知错误:{str(e0)}")
|
|
||||||
|
|
||||||
return rsp
|
|
||||||
|
|
||||||
def updateMessage(self, wxid: str, question: str, role: str) -> None:
|
|
||||||
now_time = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
||||||
|
|
||||||
time_mk = "当需要回答时间时请直接参考回复:"
|
|
||||||
# 初始化聊天记录,组装系统信息
|
|
||||||
if wxid not in self.conversation_list.keys():
|
|
||||||
question_ = [
|
|
||||||
self.system_content_msg,
|
|
||||||
{"role": "system", "content": "" + time_mk + now_time}
|
|
||||||
]
|
|
||||||
self.conversation_list[wxid] = question_
|
|
||||||
|
|
||||||
# 当前问题
|
|
||||||
content_question_ = {"role": role, "content": question}
|
|
||||||
self.conversation_list[wxid].append(content_question_)
|
|
||||||
|
|
||||||
for cont in self.conversation_list[wxid]:
|
|
||||||
if cont["role"] != "system":
|
|
||||||
continue
|
|
||||||
if cont["content"].startswith(time_mk):
|
|
||||||
cont["content"] = time_mk + now_time
|
|
||||||
|
|
||||||
# 只存储10条记录,超过滚动清除
|
|
||||||
i = len(self.conversation_list[wxid])
|
|
||||||
if i > 10:
|
|
||||||
print("滚动清除微信记录:" + wxid)
|
|
||||||
# 删除多余的记录,倒着删,且跳过第一个的系统消息
|
|
||||||
del self.conversation_list[wxid][1]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from configuration import Config
|
|
||||||
config = Config().CHATGPT
|
|
||||||
if not config:
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
chat = ChatGPT(config)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
q = input(">>> ")
|
|
||||||
try:
|
|
||||||
time_start = datetime.now() # 记录开始时间
|
|
||||||
print(chat.get_answer(q, "wxid"))
|
|
||||||
time_end = datetime.now() # 记录结束时间
|
|
||||||
|
|
||||||
print(f"{round((time_end - time_start).total_seconds(), 2)}s") # 计算的时间差为程序的执行时间,单位为秒/s
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import calendar
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from chinese_calendar import is_workday
|
|
||||||
from robot import Robot
|
|
||||||
|
|
||||||
|
|
||||||
class ReportReminder:
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def remind(robot: Robot) -> None:
|
|
||||||
|
|
||||||
receivers = robot.config.REPORT_REMINDERS
|
|
||||||
if not receivers:
|
|
||||||
receivers = ["filehelper"]
|
|
||||||
# 日报周报月报提醒
|
|
||||||
for receiver in receivers:
|
|
||||||
today = datetime.datetime.now().date()
|
|
||||||
# 如果是非工作日
|
|
||||||
if not is_workday(today):
|
|
||||||
robot.send_text_msg("休息日快乐", receiver)
|
|
||||||
# 如果是工作日
|
|
||||||
if is_workday(today):
|
|
||||||
robot.send_text_msg("该发日报啦", receiver)
|
|
||||||
# 如果是本周最后一个工作日
|
|
||||||
if ReportReminder.last_work_day_of_week(today) == today:
|
|
||||||
robot.send_text_msg("该发周报啦", receiver)
|
|
||||||
# 如果本日是本月最后一整周的最后一个工作日:
|
|
||||||
if ReportReminder.last_work_friday_of_month(today) == today:
|
|
||||||
robot.send_text_msg("该发月报啦", receiver)
|
|
||||||
|
|
||||||
# 计算本月最后一个周的最后一个工作日
|
|
||||||
@staticmethod
|
|
||||||
def last_work_friday_of_month(d: datetime.date) -> datetime.date:
|
|
||||||
days_in_month = calendar.monthrange(d.year, d.month)[1]
|
|
||||||
weekday = calendar.weekday(d.year, d.month, days_in_month)
|
|
||||||
if weekday == 4:
|
|
||||||
last_friday_of_month = datetime.date(
|
|
||||||
d.year, d.month, days_in_month)
|
|
||||||
else:
|
|
||||||
if weekday >= 5:
|
|
||||||
last_friday_of_month = datetime.date(d.year, d.month, days_in_month) - \
|
|
||||||
datetime.timedelta(days=(weekday - 4))
|
|
||||||
else:
|
|
||||||
last_friday_of_month = datetime.date(d.year, d.month, days_in_month) - \
|
|
||||||
datetime.timedelta(days=(weekday + 3))
|
|
||||||
while not is_workday(last_friday_of_month):
|
|
||||||
last_friday_of_month = last_friday_of_month - datetime.timedelta(days=1)
|
|
||||||
return last_friday_of_month
|
|
||||||
|
|
||||||
# 计算本周最后一个工作日
|
|
||||||
@staticmethod
|
|
||||||
def last_work_day_of_week(d: datetime.date) -> datetime.date:
|
|
||||||
weekday = calendar.weekday(d.year, d.month, d.day)
|
|
||||||
last_work_day_of_week = datetime.date(
|
|
||||||
d.year, d.month, d.day) + datetime.timedelta(days=(6 - weekday))
|
|
||||||
|
|
||||||
while not is_workday(last_work_day_of_week):
|
|
||||||
last_work_day_of_week = last_work_day_of_week - \
|
|
||||||
datetime.timedelta(days=1)
|
|
||||||
return last_work_day_of_week
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#! /usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from random import randint
|
|
||||||
|
|
||||||
|
|
||||||
class TigerBot:
|
|
||||||
def __init__(self, tbconf=None) -> None:
|
|
||||||
self.LOG = logging.getLogger(__file__)
|
|
||||||
self.tburl = "https://api.tigerbot.com/bot-service/ai_service/gpt"
|
|
||||||
self.tbheaders = {"Authorization": "Bearer " + tbconf["key"]}
|
|
||||||
self.tbmodel = tbconf["model"]
|
|
||||||
self.fallback = ["滚", "快滚", "赶紧滚"]
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'TigerBot'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def value_check(conf: dict) -> bool:
|
|
||||||
if conf:
|
|
||||||
return all(conf.values())
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_answer(self, msg: str, sender: str = None) -> str:
|
|
||||||
payload = {
|
|
||||||
"text": msg,
|
|
||||||
"modelVersion": self.tbmodel
|
|
||||||
}
|
|
||||||
rsp = ""
|
|
||||||
try:
|
|
||||||
rsp = requests.post(self.tburl, headers=self.tbheaders, json=payload).json()
|
|
||||||
rsp = rsp["data"]["result"][0]
|
|
||||||
except Exception as e:
|
|
||||||
self.LOG.error(f"{e}: {payload}\n{rsp}")
|
|
||||||
idx = randint(0, len(self.fallback) - 1)
|
|
||||||
rsp = self.fallback[idx]
|
|
||||||
|
|
||||||
return rsp
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from configuration import Config
|
|
||||||
c = Config()
|
|
||||||
tbot = TigerBot(c.TIGERBOT)
|
|
||||||
rsp = tbot.get_answer("你还活着?")
|
|
||||||
print(rsp)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import pyttsx3
|
|
||||||
engine = pyttsx3.init()
|
|
||||||
engine.setProperty('rate', 150)
|
|
||||||
engine.setProperty('volume', 0.9)
|
|
||||||
engine.setProperty('voice', 'zh-CN') # 选择中文语音风格
|
|
||||||
text = '你好,世界!'
|
|
||||||
engine.say(text)
|
|
||||||
engine.runAndWait()
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#! /usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from sparkdesk_web.core import SparkWeb
|
|
||||||
import re
|
|
||||||
|
|
||||||
class XinghuoWeb:
|
|
||||||
def __init__(self, xhconf=None) -> None:
|
|
||||||
|
|
||||||
self._sparkWeb = SparkWeb(
|
|
||||||
cookie=xhconf["cookie"],
|
|
||||||
fd=xhconf["fd"],
|
|
||||||
GtToken=xhconf["GtToken"],
|
|
||||||
)
|
|
||||||
self._chat = self._sparkWeb.create_continuous_chat()
|
|
||||||
# 如果有提示词
|
|
||||||
if xhconf["prompt"]:
|
|
||||||
self._chat.chat(xhconf["prompt"])
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'XinghuoWeb'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def value_check(conf: dict) -> bool:
|
|
||||||
if conf:
|
|
||||||
return all(conf.values())
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_answer(self, msg: str, sender: str = None) -> str:
|
|
||||||
answer = self._chat.chat(msg)
|
|
||||||
answer = re.sub(r'```.*?```', '', answer, flags=re.DOTALL)
|
|
||||||
answer = re.sub(r'^\s*$\n', '', answer, flags=re.MULTILINE)
|
|
||||||
return answer
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from configuration import Config
|
|
||||||
c = Config()
|
|
||||||
xinghuo = XinghuoWeb(c.XINGHUO_WEB)
|
|
||||||
question = "请根据新闻标题,按照新闻的类型(财经、彩票、房产、股票、家居、教育、科技、社会、时尚、时政、体育、星座、游戏、娱乐)进行分类;内容前加入当前日期和星期几" \
|
|
||||||
"内容格式如下:" \
|
|
||||||
"### 分类1" \
|
|
||||||
"1.#标题1" \
|
|
||||||
"2.#标题2" \
|
|
||||||
"分类之间使用--号进行分割,无内容则忽略该分组" \
|
|
||||||
"当前日期:2024年12月16日 星期一" \
|
|
||||||
"1. 标题: 安徽监狱回应李铁可否在监狱踢球" \
|
|
||||||
"2. 标题: 董明珠问雷军:你给股民分了多少钱?" \
|
|
||||||
"3. 标题: 学习贯彻中央经济工作会议精神" \
|
|
||||||
"4. 标题: 网曝阿娇知三当三" \
|
|
||||||
"5. 标题: 当我用人民币感受台湾物价" \
|
|
||||||
"6. 标题: 男子坐轮椅在高速疾驰" \
|
|
||||||
"7. 标题: 刘亦菲陈金飞酒店聚餐" \
|
|
||||||
"8. 标题: 冷冷冷冷冷你冷冷冷冷冷" \
|
|
||||||
"9. 标题: 男生表演引体向上手滑摔到女生怀里" \
|
|
||||||
"10. 标题: 74岁斯琴高娃自曝近况" \
|
|
||||||
"11. 标题: 12岁女孩感染HPV 17岁男友被抓" \
|
|
||||||
"12. 标题: 高校女生被殴打需植入钢钉?谣言" \
|
|
||||||
"13. 标题: 东京将启动上四休三工作制" \
|
|
||||||
"14. 标题: 张杰谢娜回应婚变传闻" \
|
|
||||||
"15. 标题: 董明珠:小米空调因侵权赔了格力50万" \
|
|
||||||
"16. 标题: 汪峰女友森林北晒近况" \
|
|
||||||
"17. 标题: 实习生一句小姨让同事汗流浃背" \
|
|
||||||
"18. 标题: 日本女学生在麦当劳被捅致死" \
|
|
||||||
"19. 标题: 女生用绳子编出五脏六腑" \
|
|
||||||
"20. 标题: 带南方女婿吃东北农村大席" \
|
|
||||||
"21. 标题: 俄乌均证实朝军已参与作战" \
|
|
||||||
"22. 标题: 孙颖莎带着三个保镖开心下班" \
|
|
||||||
"23. 标题: 宋佳素颜拍戏好沧桑啊" \
|
|
||||||
"24. 标题: 2岁半宝宝“呲溜滑”超快乐" \
|
|
||||||
"25. 标题: 央视曝光先享后付套路多" \
|
|
||||||
"26. 标题: #阜阳赶大集太香了吧#" \
|
|
||||||
"27. 标题: 孟子义的面具焊在脸上了" \
|
|
||||||
"28. 标题: 福建一高中生被美国藤校录取" \
|
|
||||||
"29. 标题: 2岁社牛女孩跟满屋小朋友搭话" \
|
|
||||||
"30. 标题: 王安宇 人怎么能丢这么大的脸" \
|
|
||||||
"31. 标题: 被挡在村规民约外的“外嫁女”" \
|
|
||||||
"32. 标题: 粉尘爆炸致8死8伤 官方公布调查报告" \
|
|
||||||
"33. 标题: 游客在广东发现一窝6枚恐龙蛋化石" \
|
|
||||||
"34. 标题: 胖东来回应微信小程序的官方店铺" \
|
|
||||||
"35. 标题: 张柏芝把博物馆展品戴在身上" \
|
|
||||||
"36. 标题: 长沙一大桥下浮尸已打捞身份待确认" \
|
|
||||||
"37. 标题: 陈都灵终于演上一整部剧的女妖了" \
|
|
||||||
"38. 标题: 网上下载公开信息发给间谍换钱" \
|
|
||||||
"39. 标题: 7岁异瞳女孩在学校受到同学喜爱" \
|
|
||||||
"40. 标题: 男子面试遭猥亵后发声竟遭网暴" \
|
|
||||||
"41. 标题: 乌女权示威者裸上身破坏纪念雕塑" \
|
|
||||||
"42. 标题: 王宝强喊话甄子丹一起打坏人" \
|
|
||||||
"43. 标题: 记者卧底非法屠宰场内部极度脏乱" \
|
|
||||||
"44. 标题: 印度一教师遭绑架强迫结婚" \
|
|
||||||
"45. 标题: 媒体人:高拉特在中国拿了5亿元" \
|
|
||||||
"46. 标题: 导演回应53岁的于和伟演大学生" \
|
|
||||||
"47. 标题: 山姆销售的床笠甲醛超标被罚" \
|
|
||||||
"48. 标题: 留学回国人才纳入国家统一就业体系" \
|
|
||||||
"49. 标题: 张家界长满了韩国人" \
|
|
||||||
"50. 标题: 圆通快递回应装车工因热射病去世"
|
|
||||||
rsp = xinghuo.get_answer(question)
|
|
||||||
|
|
||||||
print(rsp)
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
from zhipuai import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class ZhiPu():
|
|
||||||
def __init__(self, conf: dict) -> None:
|
|
||||||
self.api_key = conf.get("api_key")
|
|
||||||
self.model = conf.get("model", "glm-4") # 默认使用 glm-4 模型
|
|
||||||
self.client = ZhipuAI(api_key=self.api_key)
|
|
||||||
self.converstion_list = {}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def value_check(conf: dict) -> bool:
|
|
||||||
if conf and conf.get("api_key"):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'ZhiPu'
|
|
||||||
|
|
||||||
def get_answer(self, msg: str, wxid: str, **args) -> str:
|
|
||||||
self._update_message(wxid, str(msg), "user")
|
|
||||||
response = self.client.chat.completions.create(
|
|
||||||
model=self.model,
|
|
||||||
messages=self.converstion_list[wxid]
|
|
||||||
)
|
|
||||||
resp_msg = response.choices[0].message
|
|
||||||
answer = resp_msg.content
|
|
||||||
self._update_message(wxid, answer, "assistant")
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def _update_message(self, wxid: str, msg: str, role: str) -> None:
|
|
||||||
if wxid not in self.converstion_list.keys():
|
|
||||||
self.converstion_list[wxid] = []
|
|
||||||
content = {"role": role, "content": str(msg)}
|
|
||||||
self.converstion_list[wxid].append(content)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from configuration import Config
|
|
||||||
config = Config().ZHIPU
|
|
||||||
if not config:
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
zhipu = ZhiPu(config)
|
|
||||||
rsp = zhipu.get_answer("你好")
|
|
||||||
print(rsp)
|
|
||||||
59
gewechat/client.py
Normal file
59
gewechat/client.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import os
|
||||||
|
import toml
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# 假设 gewechat_client 已经安装,并且 GewechatClient 可直接导入
|
||||||
|
from gewechat_client import GewechatClient
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self, config_path=None):
|
||||||
|
# 默认配置文件路径
|
||||||
|
if config_path is None:
|
||||||
|
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.toml")
|
||||||
|
self.config_path = config_path
|
||||||
|
config = toml.load(config_path)
|
||||||
|
gewechat_cfg = config.get("Gewechat", {})
|
||||||
|
self.base_url = gewechat_cfg.get("base_url", "")
|
||||||
|
self.gewechat_token = gewechat_cfg.get("gewechat_token", "")
|
||||||
|
self.app_id = gewechat_cfg.get("app_id", "")
|
||||||
|
self.callback_url = gewechat_cfg.get("callback_url", "")
|
||||||
|
# 初始化 GewechatClient
|
||||||
|
self.client = GewechatClient(
|
||||||
|
base_url=self.base_url,
|
||||||
|
token=self.gewechat_token
|
||||||
|
)
|
||||||
|
# 登录, 自动创建二维码,扫码后自动登录
|
||||||
|
app_id, error_msg = self.client.login(app_id=self.app_id)
|
||||||
|
|
||||||
|
if error_msg:
|
||||||
|
logger.error("登录失败")
|
||||||
|
return
|
||||||
|
# 休眠等待server启动,防止回调设置失败
|
||||||
|
max_retries = 5
|
||||||
|
retry_interval = 5 # 秒
|
||||||
|
for attempt in range(1, max_retries + 1):
|
||||||
|
resp = self.client.set_callback(self.app_id, self.callback_url)
|
||||||
|
if resp and resp.get("success", False):
|
||||||
|
print(f"set_callback 成功: {resp}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logger.warning(f"set_callback 第{attempt}次失败,{retry_interval}秒后重试...")
|
||||||
|
time.sleep(retry_interval)
|
||||||
|
else:
|
||||||
|
logger.error("set_callback 多次重试后仍失败,请检查server状态。")
|
||||||
|
|
||||||
|
# 如果启动时,配置文件中的app_id为空,那么将app_id写入配置文件
|
||||||
|
if not self.app_id and app_id:
|
||||||
|
# 更新 config.toml 文件中的 app_id
|
||||||
|
config["Gewechat"]["app_id"] = app_id
|
||||||
|
with open(self.config_path, "w", encoding="utf-8") as f:
|
||||||
|
toml.dump(config, f)
|
||||||
|
logger.info(f"已将新的APP_ID: {app_id} 写入配置文件")
|
||||||
|
# 同时更新当前实例的 app_id
|
||||||
|
self.app_id = app_id
|
||||||
|
|
||||||
|
# 项目全局唯一 client 实例
|
||||||
|
gewe_client = Client().client
|
||||||
5
gewechat/config.toml
Normal file
5
gewechat/config.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[Gewechat]
|
||||||
|
base_url= "http://192.168.2.240:2531/v2/api"
|
||||||
|
gewechat_token= "cb43f52db27e4a56bb6ec7da54373582"
|
||||||
|
app_id="wx_3BC6eSHGE5xEm_hH3__7c"
|
||||||
|
callback_url="http://192.168.2.192:8999/gewechat/callback"
|
||||||
67
main.py
67
main.py
@@ -4,78 +4,17 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
import uvicorn
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
from gewechat_client import GewechatClient
|
from gewechat_client import GewechatClient
|
||||||
|
|
||||||
import socket
|
|
||||||
# 启动FastAPI服务器
|
|
||||||
# 从callback_url中提取主机和端口
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
from configuration import Config
|
from configuration import Config
|
||||||
from constants import ChatType
|
from constants import ChatType
|
||||||
|
from gewechat.api.start_server import start_fastapi_server
|
||||||
from robot import Robot
|
from robot import Robot
|
||||||
from gewechat.api.callback import router as callback_router
|
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def is_port_in_use(port, host='0.0.0.0'):
|
|
||||||
"""检查端口是否被占用"""
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
||||||
try:
|
|
||||||
s.bind((host, port))
|
|
||||||
return False
|
|
||||||
except socket.error:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def start_fastapi_server(host="0.0.0.0", port=8999):
|
|
||||||
"""启动FastAPI服务器"""
|
|
||||||
# 检查端口是否被占用
|
|
||||||
if is_port_in_use(port, host):
|
|
||||||
logger.warning(f"端口 {port} 已被占用,尝试使用其他端口")
|
|
||||||
# 尝试其他端口
|
|
||||||
for test_port in range(9000, 9100):
|
|
||||||
if not is_port_in_use(test_port, host):
|
|
||||||
port = test_port
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
logger.error("无法找到可用端口,服务器启动失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
app = FastAPI()
|
|
||||||
app.include_router(callback_router)
|
|
||||||
|
|
||||||
# 添加健康检查路由
|
|
||||||
@app.get("/health")
|
|
||||||
async def health_check():
|
|
||||||
return {"status": "ok"}
|
|
||||||
|
|
||||||
logger.info(f"正在启动FastAPI服务器,地址: http://{host}:{port}")
|
|
||||||
|
|
||||||
# 使用线程启动uvicorn服务器
|
|
||||||
server_thread = threading.Thread(
|
|
||||||
target=uvicorn.run,
|
|
||||||
args=(app,),
|
|
||||||
kwargs={"host": host, "port": port, "log_level": "info"},
|
|
||||||
daemon=True
|
|
||||||
)
|
|
||||||
server_thread.start()
|
|
||||||
logger.info(f"FastAPI 服务已在 http://{host}:{port} 启动")
|
|
||||||
logger.info(f"回调URL: http://{host}:{port}/gewechat/callback")
|
|
||||||
|
|
||||||
# 返回启动的端口,以便调用者知道实际使用的端口
|
|
||||||
return port
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"启动FastAPI服务器失败: {e}", exc_info=True)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main(chat_type: int):
|
def main(chat_type: int):
|
||||||
config = Config()
|
config = Config()
|
||||||
base_url = config.BASE_URL
|
base_url = config.BASE_URL
|
||||||
@@ -99,8 +38,8 @@ def main(chat_type: int):
|
|||||||
if error_msg:
|
if error_msg:
|
||||||
logger.error("登录失败")
|
logger.error("登录失败")
|
||||||
return
|
return
|
||||||
#休眠等待server启动,防止回调设置失败
|
# 休眠等待server启动,防止回调设置失败
|
||||||
time.sleep(10)
|
time.sleep(5)
|
||||||
resp = client.set_callback(token, callback_url)
|
resp = client.set_callback(token, callback_url)
|
||||||
print(f"set_callback:{resp}")
|
print(f"set_callback:{resp}")
|
||||||
|
|
||||||
|
|||||||
3
robot.py
3
robot.py
@@ -151,7 +151,8 @@ class Robot(Job):
|
|||||||
# 设置ROBOT功能为启用状态
|
# 设置ROBOT功能为启用状态
|
||||||
GroupBotManager.set_group_permission(msg.roomid, Feature.ROBOT, PermissionStatus.ENABLED)
|
GroupBotManager.set_group_permission(msg.roomid, Feature.ROBOT, PermissionStatus.ENABLED)
|
||||||
# 更新联系人信息
|
# 更新联系人信息
|
||||||
self.refresh_contacts()
|
# 如果是加入新群,则拉取群用户信息,并且将群加入联系人
|
||||||
|
self.client.get_chatroom_info(self.app_id, msg.roomid)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.LOG.error(f"加入新群,自动添加并开启机器人功能 error: {e}")
|
self.LOG.error(f"加入新群,自动添加并开启机器人功能 error: {e}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user