Skip to content

Commit

Permalink
Matrix capability integration
Browse files Browse the repository at this point in the history
To support Matrix as an outlet for ghi I:
-edited ghi/configuration.py to read, check and use the (optional) Matrix-settings from the .yml
-created ghi/ghimatrix.py to facilitate creating credential-files, logging onto a Matrix server and sending messages to one or more rooms on it by using the matrix-nio module
-changed some naming and matrix-specific things in most of the files
-changed the README.md en .ghy.yml.example to include the new Matrix-support
-added relevant exception-handlers

No changes to the config-file are necessary when using IRC.

It works quite well with server.py, haven't tested it with AWS though, but should work as well.

Closes gkrizek#27
  • Loading branch information
Kvaciral committed May 2, 2022
1 parent 85fa507 commit 245249e
Show file tree
Hide file tree
Showing 8 changed files with 485 additions and 80 deletions.
128 changes: 86 additions & 42 deletions README.md

Large diffs are not rendered by default.

171 changes: 166 additions & 5 deletions ghi/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import os
import yaml

SUPPORTED_OUTLETS = ["irc", "mastodon"]
SUPPORTED_OUTLETS = ["irc", "mastodon", "matrix"]
MATRIX_DEVICE_ID = "Ghi-Matrix-Bot"

class Pool(object):


def __init__(self, name, outlets, repos, shorten, ircHost, ircPort, ircSsl, ircNick, ircPassword, ircChannels,\
mastInstance, mastUser, mastPassword, mastSecPath, mastAppName, mastMergeFilter):
mastInstance, mastUser, mastPassword, mastSecPath, mastAppName, mastMergeFilter,\
matrixUser, matrixPassword, matrixServer, matrixRooms, matrixCredPath, matrixDevId):
self.name = name
self.outlets = outlets
self.repos = repos
Expand All @@ -26,10 +28,17 @@ def __init__(self, name, outlets, repos, shorten, ircHost, ircPort, ircSsl, ircN
self.mastSecPath = mastSecPath
self.mastAppName = mastAppName
self.mastMergeFilter = mastMergeFilter
self.matrixUser = matrixUser
self.matrixPassword = matrixPassword
self.matrixServer = matrixServer
self.matrixRooms = matrixRooms
self.matrixCredPath = matrixCredPath
self.matrixDevId = matrixDevId


def containsRepo(self, repo):
for configRepo in self.repos:
for configRepo in self.repos:
print(configRepo)
if repo == configRepo["name"]:
return True
return False
Expand All @@ -44,7 +53,8 @@ class GlobalConfig(object):


def __init__(self, ircHost, ircPort, ircSsl, ircNick, ircPassword, mastInstance, mastUser,\
mastPassword, mastSecPath, mastAppName, mastMergeFilter, shorten, verify, outlets):
mastPassword, mastSecPath, mastAppName, mastMergeFilter, shorten, verify, outlets,\
matrixUser, matrixPassword, matrixServer, matrixCredPath, matrixDevId):
self.ircHost = ircHost
self.ircPort = ircPort
self.ircSsl = ircSsl
Expand All @@ -59,6 +69,11 @@ def __init__(self, ircHost, ircPort, ircSsl, ircNick, ircPassword, mastInstance,
self.shorten = shorten
self.verify = verify
self.outlets = outlets
self.matrixUser = matrixUser
self.matrixPassword = matrixPassword
self.matrixServer = matrixServer
self.matrixCredPath = matrixCredPath
self.matrixDevId = matrixDevId


def getConfiguration():
Expand Down Expand Up @@ -242,6 +257,48 @@ def getConfiguration():
globalMastAppName = None
globalMastMergeFilter = None

if "matrix" in globalConfig:
if "user" in globalConfig["matrix"]:
globalMatrixUser = globalConfig["matrix"]["user"]
if type(globalMatrixUser) is not str:
raise TypeError("'user' is not a string")
else:
globalMatrixUser = None

if "password" in globalConfig["matrix"]:
globalMatrixPassword = globalConfig["matrix"]["password"]
if type(globalMatrixPassword) is not str:
raise TypeError("'password' is not a string")
else:
globalMatrixPassword = None

if "homeserver" in globalConfig["matrix"]:
globalMatrixServer = globalConfig["matrix"]["homeserver"]
if type(globalMatrixServer) is not str:
raise TypeError("'homeserver' is not a string")
else:
globalMatrixServer = None

if "credentialspath" in globalConfig["matrix"]:
globalMatrixCredPath = globalConfig["matrix"]["credentialspath"]
if type(globalMatrixCredPath) is not str:
raise TypeError("'credentialspath' is not a string")
else:
globalMatrixCredPath = None

if "device_id" in globalConfig["matrix"]:
globalMatrixDevId = globalConfig["matrix"]["device_id"]
if type(globalMatrixDevId) is not str:
raise TypeError("'device_id' is not a string")
else:
globalMatrixDevId = MATRIX_DEVICE_ID
else:
globalMatrixUser = None
globalMatrixPassword = None
globalMatrixServer = None
globalMatrixCredPath = None
globalMatrixDevId = None

if "github" in globalConfig:
if "shorten_url" in globalConfig["github"]:
globalShorten = globalConfig["github"]["shorten_url"]
Expand Down Expand Up @@ -292,6 +349,11 @@ def getConfiguration():
mastSecPath = globalMastSecPath,
mastAppName = globalMastAppName,
mastMergeFilter = globalMastMergeFilter,
matrixUser = globalMatrixUser,
matrixPassword = globalMatrixPassword,
matrixServer = globalMatrixServer,
matrixCredPath = globalMatrixCredPath,
matrixDevId = globalMatrixDevId,
shorten = globalShorten,
verify = globalVerify,
outlets = globalGeneratedOutlets
Expand Down Expand Up @@ -575,6 +637,91 @@ def getConfiguration():
else:
mastMergeFilter = True

if "matrix" in generatedOutlets and "matrix" in pool:
if "user" in pool["matrix"]:
matrixUser = pool["matrix"]["user"]
elif globalSettings.matrixUser:
matrixUser = globalSettings.matrixUser
else:
raise KeyError("user")
if type(matrixUser) is not str:
raise TypeError("'user' is not a string")

if "password" in pool["matrix"]:
matrixPassword = pool["matrix"]["password"]
elif globalSettings.matrixPassword:
matrixPassword = globalSettings.matrixPassword
else:
raise KeyError("password")
if type(matrixPassword) is not str:
raise TypeError("'password' is not a string")

if "homeserver" in pool["matrix"]:
matrixServer = pool["matrix"]["homeserver"]
elif globalSettings.matrixServer:
matrixServer = globalSettings.matrixServer
else:
raise KeyError("homeserver")
if type(matrixServer) is not str:
raise TypeError("'homeserver' is not a string")

if "credentialspath" in pool["matrix"]:
matrixCredPath = pool["matrix"]["credentialspath"]
elif globalSettings.matrixCredPath:
matrixCredPath = globalSettings.matrixCredPath
else:
raise KeyError("credentialspath")
if type(matrixCredPath) is not str:
raise TypeError("'credentialspath' is not a string")

if "device_id" in pool["matrix"]:
matrixDevId = pool["matrix"]["device_id"]
elif globalSettings.matrixDevId:
matrixDevId = globalSettings.matrixDevId
else:
matrixDevId = MATRIX_DEVICE_ID
if type(matrixDevId) is not str:
raise TypeError("'device_id' is not a string")

if "rooms" in pool["matrix"]:
matrixRooms = pool["matrix"]["rooms"]
else:
raise KeyError("rooms")
if type(matrixRooms) is not list:
raise TypeError("'rooms' is not a list")
if len(matrixRooms) < 1:
raise TypeError("'rooms' must contain at least 1 item")

generatedMatrixRooms = list()
for room in matrixRooms:
generatedMatrixRooms.append(room)#"+room)

elif "matrix" in generatedOutlets and "matrix" in globalConfig:
if globalSettings.matrixUser:
matrixUser = globalSettings.matrixUser
else:
raise KeyError("user")

if globalSettings.matrixPassword:
matrixPassword = globalSettings.matrixPassword
else:
raise KeyError("password")

if globalSettings.matrixServer:
matrixServer = globalSettings.matrixServer
else:
raise KeyError("homeserver")

if globalSettings.matrixCredPath:
matrixCredPath = globalSettings.matrixCredPath
else:
raise KeyError("credentialspath")

if globalSettings.matrixDevId:
matrixDevId = globalSettings.matrixDevId
else:
matrixDevId = MATRIX_DEVICE_ID

except (KeyError, TypeError) as e:
errorMessage = "Missing or invalid parameter in configuration file: %s" % e
logging.error(errorMessage)
Expand Down Expand Up @@ -602,6 +749,14 @@ def getConfiguration():
mastAppName = None
mastMergeFilter = None

if "matrix" not in generatedOutlets:
matrixUser = None
matrixPassword = None
matrixServer = None
generatedMatrixRooms = None
matrixCredPath = None
matrixDevId = None

pools.append(
Pool(
name=name,
Expand All @@ -619,7 +774,13 @@ def getConfiguration():
mastPassword=mastPassword,
mastSecPath=mastSecPath,
mastAppName=mastAppName,
mastMergeFilter=mastMergeFilter
mastMergeFilter=mastMergeFilter,
matrixUser=matrixUser,
matrixPassword=matrixPassword,
matrixServer=matrixServer,
matrixRooms=generatedMatrixRooms,
matrixCredPath=matrixCredPath,
matrixDevId=matrixDevId
)
)

Expand Down
17 changes: 16 additions & 1 deletion ghi/events/pull_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,25 @@ def PullRequest(payload, shorten):
url = url
)

matrixMessage = (
'[<font color="fuschia">{repo}</font>] <font color="gray">{user}</font> <b>{action}</b> pull request <b>#{number}</b>: {title} '
'(<font color="purple">{baseBranch}</font>...<font color="purple">{headBranch}</font>) <font color="blue"></u>{url}</u></font>'
).format(
repo = payload["pull_request"]["base"]["repo"]["name"],
user = payload["sender"]["login"],
action = action,
number = payload["number"],
title = payload["pull_request"]["title"],
baseBranch = payload["pull_request"]["base"]["ref"],
headBranch = payload["pull_request"]["head"]["ref"],
url = url
)

return {
"statusCode": 200,
"ircMessages": [ircMessage],
"mastMessages": [mastMessage]
"mastMessages": [mastMessage],
"matrixMessages": [matrixMessage]
}

else:
Expand Down
59 changes: 49 additions & 10 deletions ghi/events/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,35 @@ def Push(payload, poolRepos, shorten):
compareUrl = url
)

matrixMessage = (
'[<font color="fuchsia">{repo}</font>] <font color="gray">{user}</font> {action} '
'tag <font color="purple">{tag}</font>: <font color="navy"><u>{compareUrl}</u></font>'
).format(
repo = payload["repository"]["name"],
user = payload["pusher"]["name"],
action = action,
tag = ref.split("/", maxsplit=2)[2],
compareUrl = url
)

return {
"statusCode": 200,
"ircMessages": [ircMessage],
"mastMessages": [mastMessage]
"mastMessages": [mastMessage],
"matrixMessages": [matrixMessage]
}

else:
# Commits were pushed
ircMessages = []
mastMessages = []
commits = payload["commits"]
repo = payload["repository"]["name"]
fullName = payload["repository"]["full_name"]
user = payload["pusher"]["name"]
length = len(commits)
branch = ref.split("/", maxsplit=2)[2]
ircMessages = []
mastMessages = []
matrixMessages = []
commits = payload["commits"]
repo = payload["repository"]["name"]
fullName = payload["repository"]["full_name"]
user = payload["pusher"]["name"]
length = len(commits)
branch = ref.split("/", maxsplit=2)[2]

# Check if the pool has allowed branches set.
# If they do, make sure that this branch is included
Expand Down Expand Up @@ -143,6 +156,20 @@ def Push(payload, poolRepos, shorten):
)
)

matrixMessages.append(
'[<font color="fuchsia">{repo}</font>] <font color="gray">{user}</font> {action} '
'<b>{length}</b> commit{plural} to <font color="purple">{branch}</font>: '
'<font color="navy"><u>{compareUrl}</u></font>'.format(
repo = repo,
user = user,
action = action,
length = length,
branch = branch,
compareUrl = url,
plural = plural
)
)

# First 3 individual commits
num = 0
for commit in commits:
Expand Down Expand Up @@ -179,10 +206,22 @@ def Push(payload, poolRepos, shorten):
)
)

matrixMessages.append(
'<font color="fuchsia">{repo}</font>/<font color="purple">{branch}</font> '
'<font color="gray">{shortCommit}</font> <font color="lightgrey">{user}</font>: {message}'.format(
repo = repo,
branch = branch,
shortCommit = commit["id"][0:7],
user = author,
message = commitMessage
)
)

num += 1

return {
"statusCode": 200,
"ircMessages": ircMessages,
"mastMessages": mastMessages
"mastMessages": mastMessages,
"matrixMessages": matrixMessages
}
Loading

0 comments on commit 245249e

Please sign in to comment.