From 2a3faef0cb951dd3702b33cf4b0f730e5fdcfafa Mon Sep 17 00:00:00 2001 From: Zzz Date: Wed, 18 Jun 2025 14:57:32 +0200 Subject: [PATCH 1/3] add filter feature --- gitlab_matrix/types.py | 89 +++++++++++++++++++++++++++++++++++++--- gitlab_matrix/webhook.py | 79 +++++++++++++++++++++++++++-------- 2 files changed, 145 insertions(+), 23 deletions(-) diff --git a/gitlab_matrix/types.py b/gitlab_matrix/types.py index f48174f..bbfbff5 100644 --- a/gitlab_matrix/types.py +++ b/gitlab_matrix/types.py @@ -14,15 +14,20 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from typing import List, Union, Dict, Optional, Type, NewType, ClassVar, Tuple, Iterable from datetime import datetime +from typing import ClassVar, Dict, Iterable, List, NewType, Optional, Tuple, Type, Union -from jinja2 import TemplateNotFound +import attr from attr import dataclass +from jinja2 import TemplateNotFound +from mautrix.types import ( + JSON, + ExtensibleEnum, + SerializableAttrs, + deserializer, + serializer, +) from yarl import URL -import attr - -from mautrix.types import JSON, ExtensibleEnum, SerializableAttrs, serializer, deserializer from .util import contrast, hex_to_rgb @@ -929,3 +934,77 @@ def build_url(self) -> str: "BuildStatus": BuildStatus, "FailureReason": FailureReason, } + +@dataclass +class UserFilter(SerializableAttrs): + user_id: Optional[int] + user_name: Optional[str] + + +def is_userid_in_event(evt: GitlabEventType, userid:int|None): + if userid is None: + return False + inside = False + if hasattr(evt, 'user') and isinstance(evt.user, GitlabUser): + if evt.user.id is not None and evt.user.id == userid: + print("user id. Direct user") + inside = True + if isinstance(evt, (GitlabIssueEvent)) and evt.assignees is not None: + if any(user.id == userid for user in evt.assignees): + print("user id. GitlabIssueEvent assignees") + inside =True + if isinstance(evt, (GitlabPushEvent)) and evt.user_id is not None and evt.user_id == userid: + print("userid . GitlabPushEvent ") + inside =True + if isinstance(evt, (GitlabCommentEvent)): + if evt.merge_request is not None: + print("username. GitlabCommentEvent Merge") + if evt.merge_request.assignee is not None and evt.merge_request.assignee.id == userid: + print("username. GitlabCommentEvent Merge assignee ") + inside =True + if evt.issue is not None: + if evt.issue.author_id == userid: + print("userid. GitlabCommentEvent issue author") + inside =True + if evt.issue.assignee_id is not None and evt.issue.assignee_id == userid: + print("userid. GitlabCommentEvent issue assignee ") + inside =True + if evt.issue.assignee_ids is not None and userid in evt.issue.assignee_ids : + print("userid. GitlabCommentEvent issue assignees ") + inside =True + if evt.snippet is not None and evt.snippet.author_id == userid: + print("userid. GitlabCommentEvent snippet") + inside =True + + return inside + +def is_username_in_event(evt: GitlabEventType, username:str|None): + if username is None: + return False + inside = False + if hasattr(evt, 'user') and isinstance(evt.user, GitlabUser): + if evt.user.username is not None and evt.user.username == username: + print("user_name. Direct user") + inside = True + if isinstance(evt, (GitlabIssueEvent)) and evt.assignees is not None: + if any(user.username == username for user in evt.assignees): + print("username. GitlabIssueEvent assignees") + inside =True + if isinstance(evt, (GitlabPushEvent)) and evt.user_username is not None and evt.user_username == username: + print("username. GitlabPushEvent ") + inside =True + if isinstance(evt, (GitlabCommentEvent)): + if evt.merge_request is not None: + print("username. GitlabCommentEvent Merge") + if evt.merge_request.assignee is not None and evt.merge_request.assignee.username == username: + print("username. GitlabCommentEvent Merge assignee ") + inside =True + if evt.merge_request.last_commit.author is not None and evt.merge_request.last_commit.author.name == username: + print("username. GitlabCommentEvent Merge last_commit.author") + inside =True + if evt.commit is not None: + if evt.commit.author is not None and evt.commit.author.name == username: + print("username. GitlabCommentEvent commit commit.author") + inside =True + + return inside \ No newline at end of file diff --git a/gitlab_matrix/webhook.py b/gitlab_matrix/webhook.py index aa69a32..4e35b48 100644 --- a/gitlab_matrix/webhook.py +++ b/gitlab_matrix/webhook.py @@ -1,6 +1,6 @@ # gitlab - A GitLab client and webhook receiver for maubot # Copyright (C) 2019 Lorenz Steinert -# Copyright (C) 2021 Tulir Asokan +# opyright (C) 2021 Tulir Asokan # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -14,22 +14,39 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import json -from typing import List, Set, TYPE_CHECKING -from asyncio import Task import asyncio +import json import re - +from typing import List, Set +from typing import TYPE_CHECKING +from asyncio import Task import attr +from maubot.handlers import web, event +from aiohttp.web import Request, Response from jinja2 import TemplateNotFound -from aiohttp.web import Response, Request - -from mautrix.types import (EventType, RoomID, StateEvent, Membership, MessageType, JSON, - TextMessageEventContent, Format, ReactionEventContent, RelationType) +from mautrix.types import ( + JSON, + EventType, + Format, + Membership, + MessageType, + ReactionEventContent, + RelationType, + RoomID, + StateEvent, + TextMessageEventContent, +) from mautrix.util.formatter import parse_html -from maubot.handlers import web, event -from .types import GitlabJobEvent, EventParse, Action, OTHER_ENUMS +from .types import ( + OTHER_ENUMS, + Action, + EventParse, + GitlabJobEvent, + UserFilter, + is_userid_in_event, + is_username_in_event, +) from .util import TemplateManager, TemplateUtil if TYPE_CHECKING: @@ -69,6 +86,9 @@ async def post_handler(self, request: Request) -> Response: except KeyError: return Response(text="401: Unauthorized\n" "Missing auth token header\n", status=401) + + user_filter : str | None = None + userid_filter: int | None = None if token == self.bot.config["secret"]: try: room_id = RoomID(request.query["room"]) @@ -76,11 +96,25 @@ async def post_handler(self, request: Request) -> Response: return Response(text="400: Bad request\nNo room specified. " "Did you forget the ?room query parameter?\n", status=400) + try: + user_filter = request.query["username_filter"] + self.bot.log.trace(f"Query User name filter is {user_filter}") + except KeyError: + # user_filter is a optional query parameter, that will be ignored if it does not exist + pass + try: + userid_filter = int(request.query["userid_filter"]) + self.bot.log.trace(f"Query User id filter is {userid_filter}") + except KeyError: + # userid_filter is a optional query parameter, that will be ignored if it does not exist + pass + else: room_id = self.bot.db.get_webhook_room(token) if not room_id: return Response(text="401: Unauthorized\n", status=401) + filter = UserFilter(user_name= user_filter,user_id = userid_filter) try: evt_type = request.headers["X-Gitlab-Event"] except KeyError: @@ -107,14 +141,14 @@ async def post_handler(self, request: Request) -> Response: headers={"Accept": "application/json"}) self.bot.log.trace("Accepted processing of %s", request.headers["X-Gitlab-Event"]) - task = asyncio.create_task(self.try_process_hook(body, evt_type, room_id)) + task = asyncio.create_task(self.try_process_hook(body, evt_type, room_id, filter)) self.task_list += [task] return Response(status=202, text="202: Accepted\nWebhook processing started.\n") - async def try_process_hook(self, body: JSON, evt_type: str, room_id: RoomID) -> None: + async def try_process_hook(self, body: JSON, evt_type: str, room_id: RoomID, filter: UserFilter ) -> None: try: - await self.process_hook(body, evt_type, room_id) + await self.process_hook(body, evt_type, room_id, filter) except Exception: self.bot.log.warning("Failed to process webhook", exc_info=True) finally: @@ -125,10 +159,18 @@ async def try_process_hook(self, body: JSON, evt_type: str, room_id: RoomID) -> if task: self.task_list.remove(task) - async def process_hook(self, body: JSON, evt_type: str, room_id: RoomID) -> None: + async def process_hook(self, body: JSON, evt_type: str, room_id: RoomID, filter: UserFilter) -> None: msgtype = MessageType.NOTICE if self.bot.config["send_as_notice"] else MessageType.TEXT evt = EventParse[evt_type].deserialize(body) + self.bot.log.trace(f" User id filter {filter.user_id} {filter.user_name}") + if filter.user_id is None and filter.user_name is None: + send_message = True + self.bot.log.trace(" User id filter is nothing") + else: + send_message = is_userid_in_event(evt, filter.user_id) | is_username_in_event(evt, filter.user_name) + self.bot.log.trace(f" User id filter has info. {send_message}") + was_manually_handled = True if isinstance(evt, GitlabJobEvent): await self.handle_job_event(evt, evt_type, room_id) @@ -180,9 +222,10 @@ def abort() -> None: edit_evt = self.bot.db.get_event(subevt.message_id, room_id) if edit_evt: content.set_edit(edit_evt) - event_id = await self.bot.client.send_message(room_id, content) - if not edit_evt and subevt.message_id: - self.bot.db.put_event(subevt.message_id, room_id, event_id) + if send_message: + event_id = await self.bot.client.send_message(room_id, content) + if not edit_evt and subevt.message_id: + self.bot.db.put_event(subevt.message_id, room_id, event_id) async def handle_job_event(self, evt: GitlabJobEvent, evt_type: str, room_id: RoomID) -> None: push_evt = self.bot.db.get_event(evt.push_id, room_id) From 4f6144dc14e15732a2ac51f030a04718e8343756 Mon Sep 17 00:00:00 2001 From: Zzz Date: Wed, 18 Jun 2025 14:59:22 +0200 Subject: [PATCH 2/3] remove debug lines --- gitlab_matrix/types.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/gitlab_matrix/types.py b/gitlab_matrix/types.py index bbfbff5..aaa15be 100644 --- a/gitlab_matrix/types.py +++ b/gitlab_matrix/types.py @@ -947,33 +947,24 @@ def is_userid_in_event(evt: GitlabEventType, userid:int|None): inside = False if hasattr(evt, 'user') and isinstance(evt.user, GitlabUser): if evt.user.id is not None and evt.user.id == userid: - print("user id. Direct user") inside = True if isinstance(evt, (GitlabIssueEvent)) and evt.assignees is not None: if any(user.id == userid for user in evt.assignees): - print("user id. GitlabIssueEvent assignees") inside =True if isinstance(evt, (GitlabPushEvent)) and evt.user_id is not None and evt.user_id == userid: - print("userid . GitlabPushEvent ") inside =True if isinstance(evt, (GitlabCommentEvent)): if evt.merge_request is not None: - print("username. GitlabCommentEvent Merge") if evt.merge_request.assignee is not None and evt.merge_request.assignee.id == userid: - print("username. GitlabCommentEvent Merge assignee ") inside =True if evt.issue is not None: if evt.issue.author_id == userid: - print("userid. GitlabCommentEvent issue author") inside =True if evt.issue.assignee_id is not None and evt.issue.assignee_id == userid: - print("userid. GitlabCommentEvent issue assignee ") inside =True if evt.issue.assignee_ids is not None and userid in evt.issue.assignee_ids : - print("userid. GitlabCommentEvent issue assignees ") inside =True if evt.snippet is not None and evt.snippet.author_id == userid: - print("userid. GitlabCommentEvent snippet") inside =True return inside @@ -984,27 +975,20 @@ def is_username_in_event(evt: GitlabEventType, username:str|None): inside = False if hasattr(evt, 'user') and isinstance(evt.user, GitlabUser): if evt.user.username is not None and evt.user.username == username: - print("user_name. Direct user") inside = True if isinstance(evt, (GitlabIssueEvent)) and evt.assignees is not None: if any(user.username == username for user in evt.assignees): - print("username. GitlabIssueEvent assignees") inside =True if isinstance(evt, (GitlabPushEvent)) and evt.user_username is not None and evt.user_username == username: - print("username. GitlabPushEvent ") inside =True if isinstance(evt, (GitlabCommentEvent)): if evt.merge_request is not None: - print("username. GitlabCommentEvent Merge") if evt.merge_request.assignee is not None and evt.merge_request.assignee.username == username: - print("username. GitlabCommentEvent Merge assignee ") inside =True if evt.merge_request.last_commit.author is not None and evt.merge_request.last_commit.author.name == username: - print("username. GitlabCommentEvent Merge last_commit.author") inside =True if evt.commit is not None: if evt.commit.author is not None and evt.commit.author.name == username: - print("username. GitlabCommentEvent commit commit.author") inside =True return inside \ No newline at end of file From 972cfcee4fa24dfce0c5b236276d9a83ce92cb82 Mon Sep 17 00:00:00 2001 From: Zzz Date: Wed, 18 Jun 2025 15:56:15 +0200 Subject: [PATCH 3/3] change --- gitlab_matrix/types.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/gitlab_matrix/types.py b/gitlab_matrix/types.py index aaa15be..553144b 100644 --- a/gitlab_matrix/types.py +++ b/gitlab_matrix/types.py @@ -944,51 +944,48 @@ class UserFilter(SerializableAttrs): def is_userid_in_event(evt: GitlabEventType, userid:int|None): if userid is None: return False - inside = False if hasattr(evt, 'user') and isinstance(evt.user, GitlabUser): if evt.user.id is not None and evt.user.id == userid: - inside = True + return True if isinstance(evt, (GitlabIssueEvent)) and evt.assignees is not None: if any(user.id == userid for user in evt.assignees): - inside =True + return True if isinstance(evt, (GitlabPushEvent)) and evt.user_id is not None and evt.user_id == userid: - inside =True + return True if isinstance(evt, (GitlabCommentEvent)): if evt.merge_request is not None: if evt.merge_request.assignee is not None and evt.merge_request.assignee.id == userid: - inside =True + return True if evt.issue is not None: if evt.issue.author_id == userid: - inside =True + return True if evt.issue.assignee_id is not None and evt.issue.assignee_id == userid: - inside =True + return True if evt.issue.assignee_ids is not None and userid in evt.issue.assignee_ids : - inside =True + return True if evt.snippet is not None and evt.snippet.author_id == userid: - inside =True + return True - return inside + return False def is_username_in_event(evt: GitlabEventType, username:str|None): if username is None: return False - inside = False if hasattr(evt, 'user') and isinstance(evt.user, GitlabUser): if evt.user.username is not None and evt.user.username == username: - inside = True + return True if isinstance(evt, (GitlabIssueEvent)) and evt.assignees is not None: if any(user.username == username for user in evt.assignees): - inside =True + return True if isinstance(evt, (GitlabPushEvent)) and evt.user_username is not None and evt.user_username == username: - inside =True + return True if isinstance(evt, (GitlabCommentEvent)): if evt.merge_request is not None: if evt.merge_request.assignee is not None and evt.merge_request.assignee.username == username: - inside =True + return True if evt.merge_request.last_commit.author is not None and evt.merge_request.last_commit.author.name == username: - inside =True + return True if evt.commit is not None: if evt.commit.author is not None and evt.commit.author.name == username: - inside =True - - return inside \ No newline at end of file + return True + return False \ No newline at end of file