From 30150db84e9b8505f54fe2fbfe5ad803b5867fe8 Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Tue, 30 Jan 2024 17:11:47 +0800 Subject: [PATCH 01/12] add replace_at_feishu_to_github --- server/tasks/lark/issue.py | 63 +++++++++++++++++++++++++++++++++++++ server/utils/lark/parser.py | 26 --------------- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index 08d837b0..d0e06d52 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -405,6 +405,19 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): github_app, team, repo, issue, _, _ = _get_github_app( app_id, message_id, content, data, *args, **kwargs ) + + # raw_message = json.loads(data["event"]["raw_message"]) + # mentions = { + # m["key"].replace("@_user", "at_user"): m + # for m in raw_message.get("event", {}).get("message", {}).get("mentions", []) + # } + + replace_at_feishu_to_github( + data["event"]["sender"]["sender_id"]["open_id"], + team.id, + content["text"], + ) + response = github_app.create_issue_comment( team.name, repo.name, issue.issue_number, content["text"] ) @@ -415,6 +428,56 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): return response +def replace_at_feishu_to_github(openid, team_id, content): + """replace feishu at to github at. + + Args: + content (str): feishu content. + + Returns: + str: github content. + """ + + # 第一步:根据 openid 和 team_id 查询 team_member 表得到 im_user_id + im_user_id = ( + db.session.query(TeamMember.im_user_id) + .join( + IMUser, # BindUser 表是 CodeUser 和 IMUser 的别名 + IMUser.user_id == TeamMember.im_user_id, + ) + .filter( + IMUser.openid == openid, + TeamMember.team_id == team_id, + ) + .limit(1) + .scalar() + ) + + # 第二步:使用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id + if im_user_id: + code_user_id = ( + db.session.query(TeamMember.code_user_id) + .filter( + TeamMember.im_user_id == im_user_id, + TeamMember.team_id == team_id, + ) + .limit(1) + .scalar() + ) + + # 第三步:如果找到了 code_user_id,使用它在 bind_user 表中查询 name + if code_user_id: + name = ( + db.session.query(CodeUser.name) + .filter(CodeUser.user_id == code_user_id) + .scalar() + ) + + logging.info(f"code name: {name}") + + return None + + @celery.task() @with_authenticated_github() def close_issue(app_id, message_id, content, data, *args, **kwargs): diff --git a/server/utils/lark/parser.py b/server/utils/lark/parser.py index 8f5a99b7..ce6c1cc2 100644 --- a/server/utils/lark/parser.py +++ b/server/utils/lark/parser.py @@ -127,11 +127,6 @@ def init_subparsers(self): parser_reopen = self.subparsers.add_parser("/reopen") parser_reopen.set_defaults(func=self.on_reopen) - # TODO 这里实际上拿到的信息是 @_user_1,需要检查是不是当前机器人 - parser_at_bot = self.subparsers.add_parser("at_user_1") - parser_at_bot.add_argument("command", nargs="*") - parser_at_bot.set_defaults(func=self.on_at_bot) - def _get_topic_by_args(self, *args): # 新增一个判断是不是在issue/pr/repo的话题中 chat_type, topic = "", "" @@ -510,27 +505,6 @@ def on_reopen(self, param, unkown, *args, **kwargs): tasks.reopen_pull_request.delay(*args, **kwargs) return "reopen", param, unkown - def on_at_bot(self, param, unkown, *args, **kwargs): - logging.info("on_at_user_1 %r %r", vars(param), unkown) - - raw_message = args[3] - user_id = raw_message["event"]["message"]["mentions"][0]["id"]["user_id"] - user_key = raw_message["event"]["message"]["mentions"][0]["key"] - logging.info(f"user_id: {user_id}") - logging.info(f"user_key: {user_key}") - # command = param.command - content = args[2].split(" ", 1) - - # 判断机器人 - if user_key == "@_user_1" and user_id is None: - command = content[1] if len(content) > 1 else None - # 判断@bot 后续命令合法即执行 - if command: - self.parse_args(command, *args, **kwargs) - return self.on_help(param, unkown, *args, **kwargs) - - return "on_at_bot", param, unkown - def parse_args(self, command, *args, **kwargs): try: # edit可能是多行的,第一行可能没有空格 From d37fdd075fad3ccfc0bf9c866ff8c48b6a16bd45 Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Tue, 30 Jan 2024 18:46:48 +0800 Subject: [PATCH 02/12] add github name --- server/tasks/lark/issue.py | 129 ++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 44 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index d0e06d52..41f4901c 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -1,5 +1,6 @@ import json import logging +import re from celery_app import app, celery from connectai.lark.sdk import FeishuTextMessage @@ -405,21 +406,31 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): github_app, team, repo, issue, _, _ = _get_github_app( app_id, message_id, content, data, *args, **kwargs ) + comment_text = content["text"] + + # 判断 content 中是否有 at + if "mentions" in data["event"]["message"]: + # 获得 mentions 中的 openid list + mentions = data["event"]["message"]["mentions"] + openid_list = [mention["id"]["open_id"] for mention in mentions] + + # 通过 openid list 获得 code_name_list + code_name_list = get_github_name_list_by_openid_list( + openid_list, + team.id, + app_id, + message_id, + content, + data, + *args, + **kwargs, + ) - # raw_message = json.loads(data["event"]["raw_message"]) - # mentions = { - # m["key"].replace("@_user", "at_user"): m - # for m in raw_message.get("event", {}).get("message", {}).get("mentions", []) - # } - - replace_at_feishu_to_github( - data["event"]["sender"]["sender_id"]["open_id"], - team.id, - content["text"], - ) + # 替换 content 中的 im_name 为 code_name + comment_text = replace_im_name_to_github_name(content["text"], code_name_list) response = github_app.create_issue_comment( - team.name, repo.name, issue.issue_number, content["text"] + team.name, repo.name, issue.issue_number, comment_text ) if "id" not in response: return send_issue_failed_tip( @@ -428,54 +439,84 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): return response -def replace_at_feishu_to_github(openid, team_id, content): - """replace feishu at to github at. +def replace_im_name_to_github_name(content, code_name_list): + """ + replace im name to github name Args: - content (str): feishu content. + content (str): content + code_name_list (list): code name list Returns: - str: github content. + str: replaced content """ - # 第一步:根据 openid 和 team_id 查询 team_member 表得到 im_user_id - im_user_id = ( - db.session.query(TeamMember.im_user_id) - .join( - IMUser, # BindUser 表是 CodeUser 和 IMUser 的别名 - IMUser.user_id == TeamMember.im_user_id, + # 替换函数 + def replace_user(match): + index = int(match.group(1)) - 1 # 获取用户编号并转换为索引 + return ( + f"@{code_name_list[index]}" + if 0 <= index < len(code_name_list) + else match.group(0) ) + + return re.sub(r"@_user_(\d+)", replace_user, content) + + +def get_github_name_list_by_openid_list( + openid_list, team_id, app_id, message_id, content, data, *args, **kwargs +): + """ + get github name list by openid list + + Args: + openid_list (list): openid list + team_id (str): team_id + app_id (str): app_id + message_id (str): message_id + content (str): content + data (dict): data + + Returns: + list: GitHub name list + """ + # 根据 openid 列表查询 IMUser 表得到 im_user 列表 + im_user_list = ( + db.session.query(IMUser) .filter( - IMUser.openid == openid, - TeamMember.team_id == team_id, + IMUser.openid.in_(openid_list), ) - .limit(1) - .scalar() + .all() ) - # 第二步:使用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id - if im_user_id: - code_user_id = ( - db.session.query(TeamMember.code_user_id) - .filter( - TeamMember.im_user_id == im_user_id, - TeamMember.team_id == team_id, - ) - .limit(1) - .scalar() + if not im_user_list: + return send_issue_failed_tip( + "找不到对应的飞书用户", app_id, message_id, content, data, *args, **kwargs + ) + + # 用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id 列表 + code_user_ids = ( + db.session.query(TeamMember.code_user_id) + .filter( + TeamMember.im_user_id.in_([im_user.id for im_user in im_user_list]), + TeamMember.team_id == team_id, ) + .all() + ) - # 第三步:如果找到了 code_user_id,使用它在 bind_user 表中查询 name - if code_user_id: - name = ( - db.session.query(CodeUser.name) - .filter(CodeUser.user_id == code_user_id) - .scalar() + if not code_user_ids: + return send_issue_failed_tip( + "找不到对应的 GitHub 用户", app_id, message_id, content, data, *args, **kwargs ) - logging.info(f"code name: {name}") + # 用 code_user_id 列表在 bind_user 查 name 列表 + code_names = ( + db.session.query(CodeUser.name) + .filter(CodeUser.id.in_([code_user_id for code_user_id in code_user_ids])) + .all() + ) - return None + return code_names @celery.task() From 6fa5adf5c252ea593d858866398505e8bbdc4885 Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Tue, 30 Jan 2024 19:39:37 +0800 Subject: [PATCH 03/12] add feishu to github name --- server/tasks/lark/issue.py | 77 +++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index 41f4901c..dc57f301 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -413,18 +413,22 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): # 获得 mentions 中的 openid list mentions = data["event"]["message"]["mentions"] openid_list = [mention["id"]["open_id"] for mention in mentions] - - # 通过 openid list 获得 code_name_list - code_name_list = get_github_name_list_by_openid_list( - openid_list, - team.id, - app_id, - message_id, - content, - data, - *args, - **kwargs, - ) + code_name_list = [] + + for openid in openid_list: + # 通过 openid list 获得 code_name_list + code_name_list.append( + get_github_name_by_openid( + openid, + team.id, + app_id, + message_id, + content, + data, + *args, + **kwargs, + ) + ) # 替换 content 中的 im_name 为 code_name comment_text = replace_im_name_to_github_name(content["text"], code_name_list) @@ -463,14 +467,14 @@ def replace_user(match): return re.sub(r"@_user_(\d+)", replace_user, content) -def get_github_name_list_by_openid_list( - openid_list, team_id, app_id, message_id, content, data, *args, **kwargs +def get_github_name_by_openid( + openid, team_id, app_id, message_id, content, data, *args, **kwargs ): """ - get github name list by openid list + get github name by openid Args: - openid_list (list): openid list + openid (str): openid team_id (str): team_id app_id (str): app_id message_id (str): message_id @@ -478,45 +482,48 @@ def get_github_name_list_by_openid_list( data (dict): data Returns: - list: GitHub name list + str: GitHub name """ - # 根据 openid 列表查询 IMUser 表得到 im_user 列表 - im_user_list = ( - db.session.query(IMUser) + # 第一步:根据 openid 和 team_id 查询 team_member 表得到 im_user_id + im_user_id = ( + db.session.query(TeamMember.im_user_id) + .join( + IMUser, # BindUser 表是 CodeUser 和 IMUser 的别名 + IMUser.id == TeamMember.im_user_id, + ) .filter( - IMUser.openid.in_(openid_list), + IMUser.openid == openid, + TeamMember.team_id == team_id, ) - .all() + .limit(1) + .scalar() ) - if not im_user_list: + if not im_user_id: return send_issue_failed_tip( "找不到对应的飞书用户", app_id, message_id, content, data, *args, **kwargs ) - # 用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id 列表 - code_user_ids = ( + # 第二步:使用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id + code_user_id = ( db.session.query(TeamMember.code_user_id) .filter( - TeamMember.im_user_id.in_([im_user.id for im_user in im_user_list]), + TeamMember.im_user_id == im_user_id, TeamMember.team_id == team_id, ) - .all() + .limit(1) + .scalar() ) - if not code_user_ids: + if not code_user_id: return send_issue_failed_tip( "找不到对应的 GitHub 用户", app_id, message_id, content, data, *args, **kwargs ) - # 用 code_user_id 列表在 bind_user 查 name 列表 - code_names = ( - db.session.query(CodeUser.name) - .filter(CodeUser.id.in_([code_user_id for code_user_id in code_user_ids])) - .all() - ) + # 第三步:如果找到了 code_user_id,使用它在 bind_user 表中查询 name + name = db.session.query(CodeUser.name).filter(CodeUser.id == code_user_id).scalar() - return code_names + return name @celery.task() From 482192bf3e62d5f8a51adfc51d24e4eab36f2775 Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Tue, 30 Jan 2024 17:11:47 +0800 Subject: [PATCH 04/12] add replace_at_feishu_to_github --- server/tasks/lark/issue.py | 63 +++++++++++++++++++++++++++++++++++++ server/utils/lark/parser.py | 26 --------------- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index 76b7293c..ba9a8065 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -456,6 +456,19 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): github_app, team, repo, issue, _, _ = _get_github_app( app_id, message_id, content, data, *args, **kwargs ) + + # raw_message = json.loads(data["event"]["raw_message"]) + # mentions = { + # m["key"].replace("@_user", "at_user"): m + # for m in raw_message.get("event", {}).get("message", {}).get("mentions", []) + # } + + replace_at_feishu_to_github( + data["event"]["sender"]["sender_id"]["open_id"], + team.id, + content["text"], + ) + response = github_app.create_issue_comment( team.name, repo.name, issue.issue_number, content["text"] ) @@ -466,6 +479,56 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): return response +def replace_at_feishu_to_github(openid, team_id, content): + """replace feishu at to github at. + + Args: + content (str): feishu content. + + Returns: + str: github content. + """ + + # 第一步:根据 openid 和 team_id 查询 team_member 表得到 im_user_id + im_user_id = ( + db.session.query(TeamMember.im_user_id) + .join( + IMUser, # BindUser 表是 CodeUser 和 IMUser 的别名 + IMUser.user_id == TeamMember.im_user_id, + ) + .filter( + IMUser.openid == openid, + TeamMember.team_id == team_id, + ) + .limit(1) + .scalar() + ) + + # 第二步:使用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id + if im_user_id: + code_user_id = ( + db.session.query(TeamMember.code_user_id) + .filter( + TeamMember.im_user_id == im_user_id, + TeamMember.team_id == team_id, + ) + .limit(1) + .scalar() + ) + + # 第三步:如果找到了 code_user_id,使用它在 bind_user 表中查询 name + if code_user_id: + name = ( + db.session.query(CodeUser.name) + .filter(CodeUser.user_id == code_user_id) + .scalar() + ) + + logging.info(f"code name: {name}") + + return None + + @celery.task() @with_authenticated_github() def close_issue(app_id, message_id, content, data, *args, **kwargs): diff --git a/server/utils/lark/parser.py b/server/utils/lark/parser.py index 8f5a99b7..ce6c1cc2 100644 --- a/server/utils/lark/parser.py +++ b/server/utils/lark/parser.py @@ -127,11 +127,6 @@ def init_subparsers(self): parser_reopen = self.subparsers.add_parser("/reopen") parser_reopen.set_defaults(func=self.on_reopen) - # TODO 这里实际上拿到的信息是 @_user_1,需要检查是不是当前机器人 - parser_at_bot = self.subparsers.add_parser("at_user_1") - parser_at_bot.add_argument("command", nargs="*") - parser_at_bot.set_defaults(func=self.on_at_bot) - def _get_topic_by_args(self, *args): # 新增一个判断是不是在issue/pr/repo的话题中 chat_type, topic = "", "" @@ -510,27 +505,6 @@ def on_reopen(self, param, unkown, *args, **kwargs): tasks.reopen_pull_request.delay(*args, **kwargs) return "reopen", param, unkown - def on_at_bot(self, param, unkown, *args, **kwargs): - logging.info("on_at_user_1 %r %r", vars(param), unkown) - - raw_message = args[3] - user_id = raw_message["event"]["message"]["mentions"][0]["id"]["user_id"] - user_key = raw_message["event"]["message"]["mentions"][0]["key"] - logging.info(f"user_id: {user_id}") - logging.info(f"user_key: {user_key}") - # command = param.command - content = args[2].split(" ", 1) - - # 判断机器人 - if user_key == "@_user_1" and user_id is None: - command = content[1] if len(content) > 1 else None - # 判断@bot 后续命令合法即执行 - if command: - self.parse_args(command, *args, **kwargs) - return self.on_help(param, unkown, *args, **kwargs) - - return "on_at_bot", param, unkown - def parse_args(self, command, *args, **kwargs): try: # edit可能是多行的,第一行可能没有空格 From 475d493ef1f3206559753c7d9a4097fef8cc36ff Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Tue, 30 Jan 2024 18:46:48 +0800 Subject: [PATCH 05/12] add github name --- server/tasks/lark/issue.py | 128 ++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 44 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index ba9a8065..f44d9b2a 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -456,21 +456,31 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): github_app, team, repo, issue, _, _ = _get_github_app( app_id, message_id, content, data, *args, **kwargs ) + comment_text = content["text"] + + # 判断 content 中是否有 at + if "mentions" in data["event"]["message"]: + # 获得 mentions 中的 openid list + mentions = data["event"]["message"]["mentions"] + openid_list = [mention["id"]["open_id"] for mention in mentions] + + # 通过 openid list 获得 code_name_list + code_name_list = get_github_name_list_by_openid_list( + openid_list, + team.id, + app_id, + message_id, + content, + data, + *args, + **kwargs, + ) - # raw_message = json.loads(data["event"]["raw_message"]) - # mentions = { - # m["key"].replace("@_user", "at_user"): m - # for m in raw_message.get("event", {}).get("message", {}).get("mentions", []) - # } - - replace_at_feishu_to_github( - data["event"]["sender"]["sender_id"]["open_id"], - team.id, - content["text"], - ) + # 替换 content 中的 im_name 为 code_name + comment_text = replace_im_name_to_github_name(content["text"], code_name_list) response = github_app.create_issue_comment( - team.name, repo.name, issue.issue_number, content["text"] + team.name, repo.name, issue.issue_number, comment_text ) if "id" not in response: return send_issue_failed_tip( @@ -479,54 +489,84 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): return response -def replace_at_feishu_to_github(openid, team_id, content): - """replace feishu at to github at. +def replace_im_name_to_github_name(content, code_name_list): + """ + replace im name to github name Args: - content (str): feishu content. + content (str): content + code_name_list (list): code name list Returns: - str: github content. + str: replaced content """ - # 第一步:根据 openid 和 team_id 查询 team_member 表得到 im_user_id - im_user_id = ( - db.session.query(TeamMember.im_user_id) - .join( - IMUser, # BindUser 表是 CodeUser 和 IMUser 的别名 - IMUser.user_id == TeamMember.im_user_id, + # 替换函数 + def replace_user(match): + index = int(match.group(1)) - 1 # 获取用户编号并转换为索引 + return ( + f"@{code_name_list[index]}" + if 0 <= index < len(code_name_list) + else match.group(0) ) + + return re.sub(r"@_user_(\d+)", replace_user, content) + + +def get_github_name_list_by_openid_list( + openid_list, team_id, app_id, message_id, content, data, *args, **kwargs +): + """ + get github name list by openid list + + Args: + openid_list (list): openid list + team_id (str): team_id + app_id (str): app_id + message_id (str): message_id + content (str): content + data (dict): data + + Returns: + list: GitHub name list + """ + # 根据 openid 列表查询 IMUser 表得到 im_user 列表 + im_user_list = ( + db.session.query(IMUser) .filter( - IMUser.openid == openid, - TeamMember.team_id == team_id, + IMUser.openid.in_(openid_list), ) - .limit(1) - .scalar() + .all() ) - # 第二步:使用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id - if im_user_id: - code_user_id = ( - db.session.query(TeamMember.code_user_id) - .filter( - TeamMember.im_user_id == im_user_id, - TeamMember.team_id == team_id, - ) - .limit(1) - .scalar() + if not im_user_list: + return send_issue_failed_tip( + "找不到对应的飞书用户", app_id, message_id, content, data, *args, **kwargs + ) + + # 用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id 列表 + code_user_ids = ( + db.session.query(TeamMember.code_user_id) + .filter( + TeamMember.im_user_id.in_([im_user.id for im_user in im_user_list]), + TeamMember.team_id == team_id, ) + .all() + ) - # 第三步:如果找到了 code_user_id,使用它在 bind_user 表中查询 name - if code_user_id: - name = ( - db.session.query(CodeUser.name) - .filter(CodeUser.user_id == code_user_id) - .scalar() + if not code_user_ids: + return send_issue_failed_tip( + "找不到对应的 GitHub 用户", app_id, message_id, content, data, *args, **kwargs ) - logging.info(f"code name: {name}") + # 用 code_user_id 列表在 bind_user 查 name 列表 + code_names = ( + db.session.query(CodeUser.name) + .filter(CodeUser.id.in_([code_user_id for code_user_id in code_user_ids])) + .all() + ) - return None + return code_names @celery.task() From cd3af1ed41b067f94752f8f2b80b3511483f807c Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Tue, 30 Jan 2024 19:39:37 +0800 Subject: [PATCH 06/12] add feishu to github name --- server/tasks/lark/issue.py | 77 +++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index f44d9b2a..a9fffac5 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -463,18 +463,22 @@ def create_issue_comment(app_id, message_id, content, data, *args, **kwargs): # 获得 mentions 中的 openid list mentions = data["event"]["message"]["mentions"] openid_list = [mention["id"]["open_id"] for mention in mentions] - - # 通过 openid list 获得 code_name_list - code_name_list = get_github_name_list_by_openid_list( - openid_list, - team.id, - app_id, - message_id, - content, - data, - *args, - **kwargs, - ) + code_name_list = [] + + for openid in openid_list: + # 通过 openid list 获得 code_name_list + code_name_list.append( + get_github_name_by_openid( + openid, + team.id, + app_id, + message_id, + content, + data, + *args, + **kwargs, + ) + ) # 替换 content 中的 im_name 为 code_name comment_text = replace_im_name_to_github_name(content["text"], code_name_list) @@ -513,14 +517,14 @@ def replace_user(match): return re.sub(r"@_user_(\d+)", replace_user, content) -def get_github_name_list_by_openid_list( - openid_list, team_id, app_id, message_id, content, data, *args, **kwargs +def get_github_name_by_openid( + openid, team_id, app_id, message_id, content, data, *args, **kwargs ): """ - get github name list by openid list + get github name by openid Args: - openid_list (list): openid list + openid (str): openid team_id (str): team_id app_id (str): app_id message_id (str): message_id @@ -528,45 +532,48 @@ def get_github_name_list_by_openid_list( data (dict): data Returns: - list: GitHub name list + str: GitHub name """ - # 根据 openid 列表查询 IMUser 表得到 im_user 列表 - im_user_list = ( - db.session.query(IMUser) + # 第一步:根据 openid 和 team_id 查询 team_member 表得到 im_user_id + im_user_id = ( + db.session.query(TeamMember.im_user_id) + .join( + IMUser, # BindUser 表是 CodeUser 和 IMUser 的别名 + IMUser.id == TeamMember.im_user_id, + ) .filter( - IMUser.openid.in_(openid_list), + IMUser.openid == openid, + TeamMember.team_id == team_id, ) - .all() + .limit(1) + .scalar() ) - if not im_user_list: + if not im_user_id: return send_issue_failed_tip( "找不到对应的飞书用户", app_id, message_id, content, data, *args, **kwargs ) - # 用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id 列表 - code_user_ids = ( + # 第二步:使用 im_user_id 和 team_id 再次查询 team_member 表得到 code_user_id + code_user_id = ( db.session.query(TeamMember.code_user_id) .filter( - TeamMember.im_user_id.in_([im_user.id for im_user in im_user_list]), + TeamMember.im_user_id == im_user_id, TeamMember.team_id == team_id, ) - .all() + .limit(1) + .scalar() ) - if not code_user_ids: + if not code_user_id: return send_issue_failed_tip( "找不到对应的 GitHub 用户", app_id, message_id, content, data, *args, **kwargs ) - # 用 code_user_id 列表在 bind_user 查 name 列表 - code_names = ( - db.session.query(CodeUser.name) - .filter(CodeUser.id.in_([code_user_id for code_user_id in code_user_ids])) - .all() - ) + # 第三步:如果找到了 code_user_id,使用它在 bind_user 表中查询 name + name = db.session.query(CodeUser.name).filter(CodeUser.id == code_user_id).scalar() - return code_names + return name @celery.task() From 743278ca76561fd54f8b4a98186944cd0adb2859 Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Wed, 31 Jan 2024 13:05:37 +0800 Subject: [PATCH 07/12] add github at --- server/tasks/lark/issue.py | 69 ++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index a9fffac5..253afce1 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -312,10 +312,10 @@ def send_issue_comment(issue_id, comment, user_name: str): ) if chat_group and issue.message_id: bot, _ = get_bot_by_application_id(chat_group.im_application_id) - # 替换 comment 中的图片 url 为 image_key comment = replace_images_with_keys(comment, bot) - content = gen_comment_message(user_name, comment) + # 统一用富文本回答, 支持图片、at + content = gen_comment_post_message(user_name, comment) result = bot.reply( issue.message_id, FeishuPostMessage(*content), @@ -324,27 +324,74 @@ def send_issue_comment(issue_id, comment, user_name: str): return False -def gen_comment_message(user_name, comment): +def gen_comment_post_message(user_name, comment): comment = comment.replace("\r\n", "\n") comment = re.sub(r"!\[.*?\]\((.*?)\)", r"\n\1\n", comment) - pattern = r"img_v\d{1,}_\w{4}_[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}" + img_key_pattern = r"img_v\d{1,}_\w{4}_[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}" messages = [] messages.append([FeishuPostMessageText(f"@{user_name}: ")]) # 根据换行符分割 - elements = re.split("\n", comment) - for element in elements: - if not element or element == "": + lines = re.split("\n", comment) + for line in lines: + if not line or line == "": continue - if re.match(pattern, element): - messages.append([FeishuPostMessageImage(image_key=element)]) - else: # 处理文本部分 - messages.append([FeishuPostMessageText(text=element)]) + if re.match(img_key_pattern, line): + messages.append([FeishuPostMessageImage(image_key=line)]) + else: + # 处理每行 at, 普通文本 + # "test @freeziyou asdf" + elements = line.split(" ") + element_messages = [] + for element in elements: + if element.startswith("@"): + element_messages.append( + FeishuPostMessageAt( + user_id=get_openid_by_code_name(element[1:]) + ) + ) + else: + element_messages.append(FeishuPostMessageText(text=element)) + + messages.append(element_messages) return messages +def get_openid_by_code_name(code_name): + code_user_id = ( + db.session.query(CodeUser.id) + .filter( + CodeUser.name == code_name, + ) + .limit(1) + .scalar() + ) + if not code_user_id: + logging.info(f"get_openid_by_code_name---code_user_id: {code_user_id}") + return None + + openid = ( + db.session.query(IMUser.openid) + .join( + TeamMember, + TeamMember.im_user_id == IMUser.id, + ) + .filter( + TeamMember.code_user_id == code_user_id, + ) + .limit(1) + .scalar() + ) + + if not openid: + logging.info(f"get_openid_by_code_name---openid: {openid}") + return None + + return openid + + @celery.task() def update_issue_card(issue_id: str): """Update issue card message. From 10c1bb935b57fb9afc15ee6d114c23164b3579fa Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Wed, 31 Jan 2024 13:20:17 +0800 Subject: [PATCH 08/12] add github at --- server/tasks/lark/issue.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index 253afce1..b652ef38 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -329,8 +329,7 @@ def gen_comment_post_message(user_name, comment): comment = re.sub(r"!\[.*?\]\((.*?)\)", r"\n\1\n", comment) img_key_pattern = r"img_v\d{1,}_\w{4}_[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}" - messages = [] - messages.append([FeishuPostMessageText(f"@{user_name}: ")]) + messages = [[FeishuPostMessageText(f"@{user_name}: ")]] # 根据换行符分割 lines = re.split("\n", comment) @@ -341,15 +340,15 @@ def gen_comment_post_message(user_name, comment): messages.append([FeishuPostMessageImage(image_key=line)]) else: # 处理每行 at, 普通文本 - # "test @freeziyou asdf" elements = line.split(" ") element_messages = [] for element in elements: if element.startswith("@"): + user_id = get_openid_by_code_name(element[1:]) element_messages.append( - FeishuPostMessageAt( - user_id=get_openid_by_code_name(element[1:]) - ) + FeishuPostMessageAt(user_id=user_id) + if user_id + else FeishuPostMessageText(text=element) ) else: element_messages.append(FeishuPostMessageText(text=element)) From 794d18484c369d1a742adece256d1936db6b809b Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Wed, 31 Jan 2024 13:28:23 +0800 Subject: [PATCH 09/12] add logging --- server/tasks/lark/issue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/tasks/lark/issue.py b/server/tasks/lark/issue.py index b652ef38..aeedc10f 100644 --- a/server/tasks/lark/issue.py +++ b/server/tasks/lark/issue.py @@ -368,7 +368,7 @@ def get_openid_by_code_name(code_name): .scalar() ) if not code_user_id: - logging.info(f"get_openid_by_code_name---code_user_id: {code_user_id}") + logging.info(f"get_openid_by_code_name---code_user_id: Not found") return None openid = ( @@ -385,7 +385,7 @@ def get_openid_by_code_name(code_name): ) if not openid: - logging.info(f"get_openid_by_code_name---openid: {openid}") + logging.info(f"get_openid_by_code_name---openid: Not found") return None return openid From dbcd564353f575d9ecefa51282a831e4bfa25b4c Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Wed, 31 Jan 2024 15:50:18 +0800 Subject: [PATCH 10/12] fix files --- .github/workflows/docker-image.yml | 119 +++++++++++++++-------------- deploy/Dockerfile.proxy.saas | 3 +- server/utils/redis.py | 2 +- 3 files changed, 64 insertions(+), 60 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 04fa51a4..5b00a918 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,57 +1,62 @@ -name: Docker Image CI - -on: - push: - branches: [ "main" ] - paths: - - '**' - - 'website/**' - workflow_dispatch: - -jobs: - build-main-app: - runs-on: ubuntu-latest - if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && !contains(github.event.head_commit.modified, 'website')) }} - steps: - - name: Check out the repo - uses: actions/checkout@v2 - - name: Log in to Docker Hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_HUB_ACCOUNT }} - password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - - name: Build and push main app Docker image - uses: docker/build-push-action@v2 - with: - context: . - file: deploy/Dockerfile - push: true - tags: connectai/gitmaya:latest - - build-website: - runs-on: ubuntu-latest - if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.modified, 'website') }} - steps: - - name: Check out the repo - uses: actions/checkout@v2 - with: - submodules: 'true' - - name: Log in to Docker Hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_HUB_ACCOUNT }} - password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - - name: Build and push website Docker image - uses: docker/build-push-action@v2 - with: - context: . - file: deploy/Dockerfile.proxy - push: true - tags: connectai/gitmaya-proxy:latest - - name: Build and push website Docker image-SaaS - uses: docker/build-push-action@v2 - with: - context: . - file: deploy/Dockerfile.proxy.saas - push: true - tags: connectai/gitmaya-proxy:saas +name: Docker Image CI + +on: + push: + branches: [ "main" ] + paths: + - '**' + - 'website/**' + workflow_dispatch: + +jobs: + build-main-app: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && !contains(github.event.head_commit.modified, 'website')) }} + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Log in to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_ACCOUNT }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Build and push main app Docker image + uses: docker/build-push-action@v2 + with: + context: . + file: deploy/Dockerfile + push: true + tags: connectai/gitmaya:latest + + build-website: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.modified, 'website') }} + steps: + - name: Check out the repo + uses: actions/checkout@v2 + with: + submodules: 'true' + + - name: Log in to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_ACCOUNT }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Build and push website Docker image + uses: docker/build-push-action@v2 + with: + context: . + file: deploy/Dockerfile.proxy + push: true + tags: connectai/gitmaya-proxy:latest + + - name: Build and push website Docker image-SaaS + uses: docker/build-push-action@v2 + with: + context: . + file: deploy/Dockerfile.proxy.saas + push: true + tags: connectai/gitmaya-proxy:saas diff --git a/deploy/Dockerfile.proxy.saas b/deploy/Dockerfile.proxy.saas index a149a8a3..ce46c91f 100644 --- a/deploy/Dockerfile.proxy.saas +++ b/deploy/Dockerfile.proxy.saas @@ -8,8 +8,7 @@ RUN corepack enable COPY ./website /app WORKDIR /app -RUN pnpm install --frozen-lockfile -RUN pnpm build:saas +RUN pnpm install --frozen-lockfile && pnpm build:saas FROM jwilder/nginx-proxy:alpine diff --git a/server/utils/redis.py b/server/utils/redis.py index ceda407d..037ff7ad 100644 --- a/server/utils/redis.py +++ b/server/utils/redis.py @@ -10,7 +10,7 @@ app.config.setdefault("REDIS_URL", "redis://redis:6379/0") -client = redis.from_url(app.config["REDIS_URL"], decode_responses=True) +client = redis.from_url(app.config["CELERY_BROKER_URL"], decode_responses=True) class RedisStorage(object): From 0fb0ffb2edf2cc4782c19005b2c354d7cac20662 Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Wed, 31 Jan 2024 15:57:29 +0800 Subject: [PATCH 11/12] fix files --- server/utils/redis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/utils/redis.py b/server/utils/redis.py index 037ff7ad..ceda407d 100644 --- a/server/utils/redis.py +++ b/server/utils/redis.py @@ -10,7 +10,7 @@ app.config.setdefault("REDIS_URL", "redis://redis:6379/0") -client = redis.from_url(app.config["CELERY_BROKER_URL"], decode_responses=True) +client = redis.from_url(app.config["REDIS_URL"], decode_responses=True) class RedisStorage(object): From f9479b24ed4c30d4efa1b9beb3c5a68e6b68012a Mon Sep 17 00:00:00 2001 From: freeziyou <80776877@qq.com> Date: Wed, 31 Jan 2024 15:57:50 +0800 Subject: [PATCH 12/12] fix files --- server/utils/redis.py | 220 +++++++++++++++++++++--------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/server/utils/redis.py b/server/utils/redis.py index ceda407d..41a93087 100644 --- a/server/utils/redis.py +++ b/server/utils/redis.py @@ -1,110 +1,110 @@ -import asyncio -import functools -import logging -import pickle -import random -from inspect import iscoroutinefunction - -import redis -from app import app - -app.config.setdefault("REDIS_URL", "redis://redis:6379/0") - -client = redis.from_url(app.config["REDIS_URL"], decode_responses=True) - - -class RedisStorage(object): - def __init__(self, **kwargs): - for k, v in kwargs.items(): - if v: - self.set(k, v) - - def get(self, name): - return client.get(name) - - def set(self, name, value): - client.set(name, value) - - -def get_client(decode_responses=False): - return redis.from_url(app.config["REDIS_URL"], decode_responses=decode_responses) - - -def gen_prefix(obj, method): - return ".".join([obj.__module__, obj.__class__.__name__, method.__name__]) - - -def stalecache( - key=None, - expire=600, - stale=3600, - time_lock=1, - time_delay=1, - max_time_delay=10, -): - def decorate(method): - @functools.wraps(method) - def wrapper(*args, **kwargs): - if kwargs.get("skip_cache"): - return method(*args, **kwargs) - name = args[0] if args and not key else None - - res = get_client(False).pipeline().ttl(name).get(name).execute() - v = pickle.loads(res[1]) if res[0] > 0 and res[1] else None - if res[0] <= 0 or res[0] < stale: - - def func(): - value = method(*args, **kwargs) - logging.debug("update cache: %s", name) - get_client(False).pipeline().set(name, pickle.dumps(value)).expire( - name, expire + stale - ).execute() - return value - - # create new cache in blocking modal, if cache not exists. - if res[0] <= 0: - return func() - - # create new cache in non blocking modal, and return stale data. - # set expire to get a "lock", and delay to run the task - real_time_delay = random.randrange(time_delay, max_time_delay) - get_client(False).expire(name, stale + real_time_delay + time_lock) - # 创建一个 asyncio 任务来执行 func - asyncio.create_task(asyncio.sleep(real_time_delay, func())) - - return v - - @functools.wraps(method) - async def async_wrapper(*args, **kwargs): - if kwargs.get("skip_cache"): - return await method(*args, **kwargs) - - name = args[0] if args and not key else None - - res = get_client(False).pipeline().ttl(name).get(name).execute() - v = pickle.loads(res[1]) if res[0] > 0 and res[1] else None - if res[0] <= 0 or res[0] < stale: - - async def func(): - value = await method(*args, **kwargs) - logging.debug("update cache: %s", name) - get_client(False).pipeline().set(name, pickle.dumps(value)).expire( - name, expire + stale - ).execute() - return value - - # create new cache in blocking modal, if cache not exists. - if res[0] <= 0: - return await func() - - # create new cache in non blocking modal, and return stale data. - # set expire to get a "lock", and delay to run the task - real_time_delay = random.randrange(time_delay, max_time_delay) - get_client(False).expire(name, stale + real_time_delay + time_lock) - asyncio.create_task(asyncio.sleep(real_time_delay, func())) - - return v - - return async_wrapper if iscoroutinefunction(method) else wrapper - - return decorate +import asyncio +import functools +import logging +import pickle +import random +from inspect import iscoroutinefunction + +import redis +from app import app + +app.config.setdefault("REDIS_URL", "redis://redis:6379/0") + +client = redis.from_url(app.config["REDIS_URL"], decode_responses=True) + + +class RedisStorage(object): + def __init__(self, **kwargs): + for k, v in kwargs.items(): + if v: + self.set(k, v) + + def get(self, name): + return client.get(name) + + def set(self, name, value): + client.set(name, value) + + +def get_client(decode_responses=False): + return redis.from_url(app.config["REDIS_URL"], decode_responses=decode_responses) + + +def gen_prefix(obj, method): + return ".".join([obj.__module__, obj.__class__.__name__, method.__name__]) + + +def stalecache( + key=None, + expire=600, + stale=3600, + time_lock=1, + time_delay=1, + max_time_delay=10, +): + def decorate(method): + @functools.wraps(method) + def wrapper(*args, **kwargs): + if kwargs.get("skip_cache"): + return method(*args, **kwargs) + name = args[0] if args and not key else None + + res = get_client(False).pipeline().ttl(name).get(name).execute() + v = pickle.loads(res[1]) if res[0] > 0 and res[1] else None + if res[0] <= 0 or res[0] < stale: + + def func(): + value = method(*args, **kwargs) + logging.debug("update cache: %s", name) + get_client(False).pipeline().set(name, pickle.dumps(value)).expire( + name, expire + stale + ).execute() + return value + + # create new cache in blocking modal, if cache not exists. + if res[0] <= 0: + return func() + + # create new cache in non blocking modal, and return stale data. + # set expire to get a "lock", and delay to run the task + real_time_delay = random.randrange(time_delay, max_time_delay) + get_client(False).expire(name, stale + real_time_delay + time_lock) + # 创建一个 asyncio 任务来执行 func + asyncio.create_task(asyncio.sleep(real_time_delay, func())) + + return v + + @functools.wraps(method) + async def async_wrapper(*args, **kwargs): + if kwargs.get("skip_cache"): + return await method(*args, **kwargs) + + name = args[0] if args and not key else None + + res = get_client(False).pipeline().ttl(name).get(name).execute() + v = pickle.loads(res[1]) if res[0] > 0 and res[1] else None + if res[0] <= 0 or res[0] < stale: + + async def func(): + value = await method(*args, **kwargs) + logging.debug("update cache: %s", name) + get_client(False).pipeline().set(name, pickle.dumps(value)).expire( + name, expire + stale + ).execute() + return value + + # create new cache in blocking modal, if cache not exists. + if res[0] <= 0: + return await func() + + # create new cache in non blocking modal, and return stale data. + # set expire to get a "lock", and delay to run the task + real_time_delay = random.randrange(time_delay, max_time_delay) + get_client(False).expire(name, stale + real_time_delay + time_lock) + asyncio.create_task(asyncio.sleep(real_time_delay, func())) + + return v + + return async_wrapper if iscoroutinefunction(method) else wrapper + + return decorate