From f0235daf918ef832e3c712ebf86671aaa162ea9a Mon Sep 17 00:00:00 2001
From: Lorenz Steinert <lorenz@steinerts.de>
Date: Sun, 1 Sep 2019 18:11:53 +0200
Subject: [PATCH] make push hook work

---
 base-config.yaml   |   1 +
 gitlab/__init__.py | 138 ++++++++++++++++++++++++++++++++++++++++-----
 maubot.yaml        |   2 +-
 3 files changed, 127 insertions(+), 14 deletions(-)

diff --git a/base-config.yaml b/base-config.yaml
index 2851a0b..06d58f4 100644
--- a/base-config.yaml
+++ b/base-config.yaml
@@ -2,3 +2,4 @@ path: "/webhooks"
 port: 29313
 secret: "passwd"
 base_command: "gitlab"
+send_as_notice: true
diff --git a/gitlab/__init__.py b/gitlab/__init__.py
index 0f16aec..1c65591 100644
--- a/gitlab/__init__.py
+++ b/gitlab/__init__.py
@@ -1,12 +1,89 @@
 import asyncio
 
-from typing import List, Type
+from typing import List, Type, Awaitable
 
 from aiohttp import web
 
+from maubot.handlers import event
+
+from mautrix.types import (EventType, EventID, MessageType, TextMessageEventContent, Format)
+
 from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper
 
-from maubot import Plugin
+from maubot import Plugin, MessageEvent
+
+from maubot.matrix import parse_markdown
+
+
+def handlePushEvent(body) -> str:
+    branch = body['ref'].replace('refs/heads/', '')
+
+    if int(body['total_commits_count']) == 0:
+        msg = "\[{2!s}/{3!s}\] {4!s} force pushed to" \
+                   "or deleted branch [{1!s}]({0!s}/tree/{1!s})"
+        return msg.format(body['project']['web_url'],
+                          branch,
+                          body['project']['namespace'],
+                          body['project']['name'],
+                          body['user_username']
+                          )
+
+    pluralizer: str = ''
+    if int(body['total_commits_count']) != 1:
+        pluralizer = 's'
+
+    msg = "\[[{2!s}/{3!s}]({0!s}/tree/{1!s})\] " \
+          "{4:d} new commit{6!s} by {5!s}\n\n"
+    msg = msg.format(body['project']['web_url'],
+                     branch,
+                     body['project']['namespace'],
+                     body['project']['name'],
+                     body['total_commits_count'],
+                     body['user_username'],
+                     pluralizer
+                     )
+
+    for commit in reversed(body['commits']):
+        lines = commit['message'].split('\n')
+        if len(lines) > 1 and len(''.join(lines[1:])) > 0:
+            lines[0] += " (...)"
+        msg += "+ {0!s} ({1!s})\n".format(lines[0], commit['id'][:8])
+
+    return msg
+
+
+def handleTagEvent(body):
+    pass
+
+
+def handleIssueEvent(body):
+    pass
+
+
+def handleNoteEvent(body):
+    pass
+
+
+def handleMergeRequestEvent(body):
+    pass
+
+
+def handleWikiPageEvent(body):
+    pass
+
+
+def handlePipelineEvent(body):
+    pass
+
+
+EventParse = {'Push Hook': handlePushEvent,
+              'Tag Push Hook': handleTagEvent,
+              'Issue Hook': handleIssueEvent,
+              'Note Hook': handleNoteEvent,
+              'Merge Request Hook': handleMergeRequestEvent,
+              'Wiki Page Hook': handleWikiPageEvent,
+              'Pipeline Hook': handlePipelineEvent
+              }
 
 
 class Config(BaseProxyConfig):
@@ -15,19 +92,47 @@ class Config(BaseProxyConfig):
         helper.copy("port")
         helper.copy("secret")
         helper.copy("base_command")
+        helper.copy("send_as_notice")
 
 
 class Gitlab(Plugin):
 
     routes = web.RouteTableDef()
 
-    async def process_hook(self, request: web.Request) -> None:
-        if not request.has_body():
-            await self.client.send_text(request.query['room'],
-                                        "Webhook doesn't have a Body.")
-        self.log.debug(str(request))
-        self.log.debug(str(request.query['room']))
-        await self.client.send_text(request.query['room'], str(request))
+    def send_gitlab_event(self, room: str, msg: str) -> Awaitable[EventID]:
+        if self.config['send_as_notice']:
+            msgtype = MessageType.NOTICE
+        else:
+            msgtype = MessageType.TEXT
+
+        content = TextMessageEventContent(msgtype=msgtype,
+                                          body=msg
+                                          )
+        content.format = Format.HTML
+        content.body, content.formatted_body = parse_markdown(content.body,
+                                                              allow_html=True
+                                                              )
+        return self.client.send_message_event(room,
+                                              EventType.ROOM_MESSAGE,
+                                              content
+                                              )
+
+    async def process_hook(self, req: web.Request) -> None:
+        if not req.has_body:
+            self.log.debug('no body')
+            return
+
+        body = await req.json()
+
+        if 'X-Gitlab-Event' not in req.headers:
+            self.log.debug('missing X-Gitlab-Event Header')
+            return None
+
+        GitlabEvent = req.headers['X-Gitlab-Event']
+
+        msg = EventParse[GitlabEvent](body)
+
+        await self.send_gitlab_event(req.query['room'], msg)
 
     async def post_handler(self, request: web.Request) -> web.Response:
         # check the authorisation of the request
@@ -48,9 +153,7 @@ class Gitlab(Plugin):
                                 )
 
         # check if the bot is in the specified room
-        # TODO: make joined_rooms a clas property which is updated on startup and room join/leave
-        joined_rooms = await self.client.get_joined_rooms()
-        if request.query['room'] not in joined_rooms:
+        if request.query['room'] not in self.joined_rooms:
             resp_text = 'The Bot is not in the room.'
             return web.Response(text=resp_text,
                                 status=403
@@ -63,13 +166,18 @@ class Gitlab(Plugin):
             return web.Response(status=406,
                                 headers={'Content-Type': 'application/json'}
                                 )
-        self.task_list.append(asyncio.create_task(self.process_hook(request)))
+
+        task = self.loop.create_task(self.process_hook(request))
+        self.task_list += [task]
+        await task
 
         return web.Response(status=202)
 
     async def start(self) -> None:
         self.config.load_and_update()
 
+        self.joined_rooms = await self.client.get_joined_rooms()
+
         self.task_list: List[asyncio.Task] = []
 
         self.app = web.Application()
@@ -87,6 +195,10 @@ class Gitlab(Plugin):
             await asyncio.wait_for(task, timeout=1.0)
         await self.runner.cleanup()
 
+    @event.on(EventType.ROOM_MEMBER)
+    async def member_handler(self, evt: MessageEvent) -> None:
+        pass
+
     @classmethod
     def get_config_class(cls) -> Type[BaseProxyConfig]:
         return Config
diff --git a/maubot.yaml b/maubot.yaml
index 15c930c..810b1c9 100644
--- a/maubot.yaml
+++ b/maubot.yaml
@@ -31,7 +31,7 @@ extra_files:
 
 # List of dependencies
 #dependencies:
-#- foo
+#-
 
 #soft_dependencies:
 #- bar>=0.1
-- 
GitLab