From f1d6d53036f36a066070df90f8efa7247e3322fc Mon Sep 17 00:00:00 2001
From: Tulir Asokan <tulir@maunium.net>
Date: Sat, 30 Sep 2017 18:34:55 +0300
Subject: [PATCH] Add issue reading and save transaction IDs between sessions

---
 README.md          |  2 +-
 command-handler.go | 10 +++---
 commands.go        | 83 ++++++++++++++++++++++++++++++++++++++--------
 config.go          |  9 ++---
 matrix.go          | 13 ++++++--
 5 files changed, 91 insertions(+), 26 deletions(-)

diff --git a/README.md b/README.md
index 283dc01..9b1a9b9 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ A Gitlab bot for Matrix. It uses
 	* [x] View commit diffs
 	* [x] View commit history
 * [ ] Issue management
-	* [ ] Read issues
+	* [x] Read issues
 	* [ ] Create/close/reopen issues
 	* [ ] Read comments on issues
 	* [ ] Comment on issues
diff --git a/command-handler.go b/command-handler.go
index eb03719..81bab32 100644
--- a/command-handler.go
+++ b/command-handler.go
@@ -22,22 +22,22 @@ import (
 
 var permittedPreloginCommands = []string{"ping", "server", "login", "help"}
 
-func handleCommand(room *mautrix.Room, sender, command string, args ...string) {
+func handleCommand(room *mautrix.Room, sender, command string, args []string, lines []string) {
 	git := getGitlabClient(sender)
 	handler, ok := Commands[command]
 	if !ok {
-		UnknownCommand(git, room, sender, command, args...)
+		UnknownCommand(git, room, sender, command, args)
 		return
 	}
 	if git == nil {
 		for _, cmd := range permittedPreloginCommands {
 			if cmd == command {
-				handler(nil, room, sender, args...)
+				handler(nil, room, sender, args, lines)
 				return
 			}
 		}
-		AuthOnlyCommand(room, sender, command, args...)
+		AuthOnlyCommand(room, sender, command, args)
 		return
 	}
-	handler(git, room, sender, args...)
+	handler(git, room, sender, args, lines)
 }
diff --git a/commands.go b/commands.go
index 0627a65..4d81359 100644
--- a/commands.go
+++ b/commands.go
@@ -28,17 +28,17 @@ import (
 )
 
 // UnknownCommand handles unknown !gitlab commands.
-func UnknownCommand(git *gitlab.Client, room *mautrix.Room, sender, command string, args ...string) {
+func UnknownCommand(git *gitlab.Client, room *mautrix.Room, sender, command string, args []string) {
 	room.SendHTML("Unknown command. Type <code>!gitlab help</code> for help.")
 }
 
 // AuthOnlyCommand handles !gitlab commands that require authentication when the user hasn't logged in.
-func AuthOnlyCommand(room *mautrix.Room, sender, command string, args ...string) {
+func AuthOnlyCommand(room *mautrix.Room, sender, command string, args []string) {
 	room.SendHTML("That command can only be used if you're logged in.\nTry <code>!gitlab login &lt;access token&gt;</code>")
 }
 
 // GitlabCommand is a function that handles a !gitlab command.
-type GitlabCommand func(git *gitlab.Client, room *mautrix.Room, sender string, args ...string)
+type GitlabCommand func(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string)
 
 // Commands contains all the normal !gitlab commands.
 var Commands = map[string]GitlabCommand{
@@ -50,18 +50,19 @@ var Commands = map[string]GitlabCommand{
 	"diff":   commandDiff,
 	"log":    commandLog,
 	"whoami": commandWhoami,
+	"issue":  commandIssue,
 	"help":   commandHelp,
 }
 
-func commandPing(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandPing(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	room.Send("Pong.")
 }
 
-func commandServer(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandServer(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	room.Sendf("I'm using the GitLab server at %s", config.GitLab.Domain)
 }
 
-func commandLogin(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandLogin(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	if git != nil {
 		room.Send("You're already logged in.")
 		return
@@ -72,14 +73,26 @@ func commandLogin(git *gitlab.Client, room *mautrix.Room, sender string, args ..
 	room.Send(loginGitlab(sender, args[0]))
 }
 
-func commandLogout(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandLogout(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	logoutGitlab(sender)
 	room.Send("Access token removed successfully.")
 }
 
-func commandHelp(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandHelp(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	if git != nil {
-		room.SendHTML(`<pre><code>Commands are prefixed with !gitlab
+		if len(args) > 0 {
+			if args[0] == "issue" {
+				room.SendHTML(`<pre><code>Commands are prefixed with !gitlab issue
+- open &lt;repo&gt; &lt;title&gt;           - Open an issue. The issue body can be placed on a new line.
+- close &lt;repo&gt; &lt;id&gt;             - Close an issue.
+- comment &lt;repo&gt; &lt;id&gt; &lt;message&gt; - Comment on an issue.
+- read &lt;repo&gt; &lt;id&gt;              - Read an issue.
+- read-comments &lt;repo&gt; &lt;id&gt;     - Read comments on an issue.
+</code></pre>`)
+				return
+			}
+		}
+		fmt.Println(room.SendHTML(`<pre><code>Commands are prefixed with !gitlab
 - ping                  - Ping the bot.
 - show &lt;repo&gt; &lt;hash&gt;    - Get details about a specific commit.
 - diff &lt;repo&gt; &lt;hash&gt;    - Get the diff of a specific commit.
@@ -87,8 +100,9 @@ func commandHelp(git *gitlab.Client, room *mautrix.Room, sender string, args ...
 - whoami                - Check who you're logged in as.
 - logout                - Remove your GitLab access token from storage.
 - login &lt;token&gt;         - Add a GitLab access token to storage.
+- issue &lt;...&gt;           - Manage issues. Type !gitlab help issue for details.
 - help                  - Show this help page.
-</code></pre>`)
+</code></pre>`))
 	} else {
 		room.SendHTML(`<b>You're not logged in.</b><br/>
 <pre><code>Commands are prefixed with !gitlab
@@ -100,7 +114,7 @@ func commandHelp(git *gitlab.Client, room *mautrix.Room, sender string, args ...
 	}
 }
 
-func commandWhoami(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandWhoami(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	user, _, err := git.Users.CurrentUser()
 	if err != nil {
 		room.Sendf("Unexpected error: %s", err)
@@ -114,7 +128,7 @@ func commandWhoami(git *gitlab.Client, room *mautrix.Room, sender string, args .
 		user.Name)
 }
 
-func commandShow(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandShow(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	if len(args) < 2 {
 		room.SendHTML("Usage: <code>!gitlab show &lt;repo&gt; &lt;hash&gt;</code>")
 		return
@@ -136,7 +150,7 @@ func commandShow(git *gitlab.Client, room *mautrix.Room, sender string, args ...
 
 var diffLocationRegex = regexp.MustCompile("(@@ -[0-9]+,[0-9]+ \\+[0-9]+,[0-9]+ @@)")
 
-func commandDiff(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandDiff(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	if len(args) < 2 {
 		room.SendHTML("Usage: <code>!gitlab diff &lt;repo&gt; &lt;hash&gt;</code>")
 		return
@@ -170,7 +184,7 @@ func commandDiff(git *gitlab.Client, room *mautrix.Room, sender string, args ...
 	room.SendHTML(buf.String())
 }
 
-func commandLog(git *gitlab.Client, room *mautrix.Room, sender string, args ...string) {
+func commandLog(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
 	if len(args) == 0 {
 		room.SendHTML("Usage: <code>!gitlab log &lt;repo&gt; [n] [page]</code>")
 		return
@@ -204,3 +218,44 @@ func commandLog(git *gitlab.Client, room *mautrix.Room, sender string, args ...s
 
 	room.SendHTML(buf.String())
 }
+
+func commandIssue(git *gitlab.Client, room *mautrix.Room, sender string, args []string, lines []string) {
+	if len(args) == 0 {
+		room.SendHTML("Unknown subcommand. Try <code>!gitlab help issue</code> for help.")
+		return
+	}
+
+	switch args[0] {
+	case "read":
+		if len(args) < 3 {
+			room.Send("Usage: !gitlab issue read <repo> <issue id>")
+			return
+		}
+
+		id, err := strconv.Atoi(args[2])
+		if err != nil {
+			room.Send("Usage: !gitlab issue read <repo> <issue id>")
+			return
+		}
+
+		issue, _, err := git.Issues.GetIssue(args[1], id)
+		if err != nil {
+			room.Sendf("An error occurred: %s", err)
+			return
+		}
+
+		var buf bytes.Buffer
+		fmt.Fprintf(&buf, "Issue #%[2]d by %[3]s: <a href='%[1]s'>%[4]s</a>.", issue.WebURL, issue.IID, issue.Author.Name, issue.Title)
+		if len(issue.Assignee.Name) > 0 {
+			fmt.Fprintf(&buf, " Assigned to %s.", issue.Assignee.Name)
+		}
+		fmt.Fprintf(&buf, "<br/>\n<blockquote>%s</blockquote><br/>\n", strings.Replace(issue.Description, "\n", "<br/>\n", -1))
+		room.SendHTML(buf.String())
+	case "open":
+	case "close":
+	case "comment":
+	case "read-comments":
+	default:
+		room.SendHTML("Unknown subcommand. Try <code>!gitlab help issue</code> for help.")
+	}
+}
diff --git a/config.go b/config.go
index 4e29330..7de160f 100644
--- a/config.go
+++ b/config.go
@@ -30,10 +30,11 @@ type Config struct {
 	} `json:"webhook"`
 
 	Matrix struct {
-		Homeserver string `json:"homeserver"`
-		Username   string `json:"username"`
-		Password   string `json:"password"`
-		AuthToken  string `json:"authtoken"`
+		Homeserver    string `json:"homeserver"`
+		Username      string `json:"username"`
+		Password      string `json:"password"`
+		AuthToken     string `json:"authtoken,omitempty"`
+		TransactionID int    `json:"txnID,omitempty"`
 	}
 
 	GitLab struct {
diff --git a/matrix.go b/matrix.go
index bc259fb..f1a530a 100644
--- a/matrix.go
+++ b/matrix.go
@@ -37,6 +37,7 @@ func startMatrix() func() {
 		saveConfig(*configPath)
 	} else {
 		mxbot.SetToken(config.Matrix.Username, config.Matrix.AuthToken)
+		mxbot.TxnID = config.Matrix.TransactionID
 	}
 
 	fmt.Println("Connected to Matrix homeserver at", config.Matrix.Homeserver, "as", config.Matrix.Username)
@@ -59,13 +60,19 @@ func startMatrix() func() {
 						continue Loop
 					}
 					msg = strings.TrimPrefix(msg, "!gitlab ")
-					parts := strings.Split(msg, " ")
+					lines := strings.Split(msg, "\n")
+					parts := strings.Split(lines[0], " ")
+					if len(lines) > 1 {
+						lines = lines[1:]
+					} else {
+						lines = []string{}
+					}
 					cmd := parts[0]
 					var args []string
 					if len(parts) > 1 {
 						args = parts[1:]
 					}
-					handleCommand(evt.Room, evt.Sender, cmd, args...)
+					handleCommand(evt.Room, evt.Sender, cmd, args, lines)
 				}
 			case roomID := <-mxbot.InviteChan:
 				invite := mxbot.Invites[roomID]
@@ -78,5 +85,7 @@ func startMatrix() func() {
 
 	return func() {
 		stop <- true
+		config.Matrix.TransactionID = mxbot.TxnID
+		saveConfig(*configPath)
 	}
 }
-- 
GitLab