Compare commits
12 commits
Author | SHA1 | Date | |
---|---|---|---|
Morgan McMillian | 409b2a5a3c | ||
Morgan McMillian | 84f3882466 | ||
Morgan McMillian | 1a5c9d84b0 | ||
Morgan McMillian | a26759bb7b | ||
Morgan McMillian | 782c3d070b | ||
Morgan McMillian | 037fee7796 | ||
Morgan McMillian | 03ba94ecb9 | ||
Morgan McMillian | de7f4f5c35 | ||
Morgan McMillian | 32d38bc005 | ||
Morgan McMillian | 45f621f9af | ||
Morgan McMillian | edd1ef6212 | ||
Morgan McMillian | 82c2ab105a |
15
.dockerignore
Normal file
15
.dockerignore
Normal file
|
@ -0,0 +1,15 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
*.yaml
|
||||
*.db
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.vscode/
|
||||
.git/
|
||||
.gitignore
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
snap/
|
||||
Jenkinsfile
|
||||
.gitlab-ci.yml
|
||||
contrib/
|
56
.gitlab-ci.yml
Normal file
56
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,56 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
|
||||
|
||||
# Build a Docker image with CI/CD and push to the GitLab registry.
|
||||
# Docker-in-Docker documentation: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
|
||||
#
|
||||
# This template uses one generic job with conditional builds
|
||||
# for the default branch and all other (MR) branches.
|
||||
stages:
|
||||
- build
|
||||
|
||||
docker-build:
|
||||
# Use the official docker image.
|
||||
image: docker:latest
|
||||
stage: build
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
# Default branch leaves tag empty (= latest tag)
|
||||
# All other branches are tagged with the escaped branch name (commit ref slug)
|
||||
script:
|
||||
- |
|
||||
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
|
||||
tag=""
|
||||
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
|
||||
else
|
||||
tag=":$CI_COMMIT_REF_SLUG"
|
||||
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||
fi
|
||||
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
|
||||
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
||||
# Run this job in a branch where a Dockerfile exists
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
||||
# snap-build-arm64:
|
||||
# stage: build
|
||||
# tags:
|
||||
# - snap-arm64
|
||||
# script:
|
||||
# - snapcraft
|
||||
# - snapcraft upload --release=edge *.snap
|
||||
#
|
||||
# snap-build-amd64:
|
||||
# stage: build
|
||||
# tags:
|
||||
# - snap-amd64
|
||||
# script:
|
||||
# - snapcraft
|
||||
# - snapcraft upload --release=edge *.snap
|
|
@ -4,6 +4,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Docker image
|
||||
|
||||
### Fixed
|
||||
- Self-invite when sending a PM from a pnut app
|
||||
|
||||
## [1.3.0] - 2022-08-20
|
||||
### Added
|
||||
|
|
16
Dockerfile
16
Dockerfile
|
@ -1,16 +1,14 @@
|
|||
FROM python:3
|
||||
|
||||
VOLUME /data
|
||||
FROM python:3.11-slim-bookworm
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
RUN apt-get update && apt-get install libmagic-dev curl -y
|
||||
|
||||
COPY . .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
ENV CONFIG_FILE=/data/config.yaml
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
|
||||
EXPOSE 5000/tcp
|
||||
|
||||
CMD [ "python", "/usr/src/app/pnut-matrix.py", "-d" ]
|
||||
EXPOSE 5000
|
||||
CMD [ "python", "/usr/src/app/pnut-matrix.py" ]
|
||||
|
|
|
@ -43,7 +43,7 @@ curl --data '{"type": "m.login.application_service", "username": "your_sender_lo
|
|||
|
||||
## Contributing and support
|
||||
|
||||
You can open issues for bugs or feature requests and you can submit merge requests to this project on [GitLab]. You can also submit issues and patches to the [mailing list] or directly to [morgan@mcmillian.dev].
|
||||
You can open issues for bugs or feature requests and you can submit merge requests to this project on [GitLab]. You can also submit issues and patches directly to [morgan@mcmillian.dev].
|
||||
|
||||
Join my public chat room for development discussion.
|
||||
|
||||
|
@ -59,7 +59,6 @@ GPLv3, see [LICENSE].
|
|||
[synapse installation instructions]: https://matrix-org.github.io/synapse/latest/setup/installation.html
|
||||
[syanpse configuration]: https://matrix-org.github.io/synapse/latest/application_services.html
|
||||
[GitLab]: https://gitlab.com/thrrgilag/pnut-matrix/
|
||||
[mailing list]: https://lists.sr.ht/~thrrgilag/pnut-matrix
|
||||
[morgan@mcmillian.dev]: mailto:morgan@mcmillian.dev
|
||||
[pnut-matrix on pnut.io]: https://patter.chat/999
|
||||
[#pnut_999:pnut-matrix.dreamfall.space]: https://matrix.to/#/#pnut_999:pnut-matrix.dreamfall.space
|
||||
|
|
|
@ -29,6 +29,7 @@ def forbidden(error):
|
|||
def shutdown_session(exception=None):
|
||||
db_session.remove()
|
||||
|
||||
@app.route("/_matrix/app/v1/rooms/<alias>")
|
||||
@app.route("/rooms/<alias>")
|
||||
def query_alias(alias):
|
||||
alias_localpart = alias.split(":")[0][1:]
|
||||
|
@ -64,9 +65,11 @@ def query_alias(alias):
|
|||
else:
|
||||
abort(401)
|
||||
|
||||
url = app.config['MATRIX_HOST'] + '/_matrix/client/api/v1/createRoom?access_token='
|
||||
url += app.config['MATRIX_AS_TOKEN']
|
||||
headers = {"Content-Type":"application/json"}
|
||||
url = app.config['MATRIX_HOST'] + '/_matrix/client/api/v1/createRoom'
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer " + app.config['MATRIX_AS_TOKEN']
|
||||
}
|
||||
r = requests.post(url, headers=headers, data=json.dumps(room))
|
||||
if r.status_code == 200:
|
||||
pnutpy.api.subscribe_channel(channel_id)
|
||||
|
@ -79,6 +82,12 @@ def query_alias(alias):
|
|||
db_session.add(rr)
|
||||
db_session.commit()
|
||||
|
||||
else:
|
||||
logger.error("Unable to create room")
|
||||
logger.error(r.status_code)
|
||||
logger.error(r.text)
|
||||
abort(400)
|
||||
|
||||
except pnutpy.errors.PnutPermissionDenied:
|
||||
abort(401)
|
||||
|
||||
|
@ -88,6 +97,7 @@ def query_alias(alias):
|
|||
|
||||
return jsonify({})
|
||||
|
||||
@app.route("/_matrix/app/v1/transactions/<transaction>", methods=["PUT"])
|
||||
@app.route("/transactions/<transaction>", methods=["PUT"])
|
||||
def on_receive_events(transaction):
|
||||
|
||||
|
@ -382,7 +392,7 @@ def delete_message(event, user):
|
|||
if e is None:
|
||||
logger.debug("- can't find the event to remove -")
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
r, meta = pnutpy.api.delete_message(e.pnut_chan_id, e.pnut_msg_id)
|
||||
e.deleted = True
|
||||
|
@ -436,9 +446,11 @@ def create_room(channel, user):
|
|||
else:
|
||||
abort(401)
|
||||
|
||||
url = app.config['MATRIX_HOST'] + '/_matrix/client/api/v1/createRoom?access_token='
|
||||
url += app.config['MATRIX_AS_TOKEN']
|
||||
headers = {"Content-Type":"application/json"}
|
||||
url = app.config['MATRIX_HOST'] + '/_matrix/client/api/v1/createRoom'
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer " + app.config['MATRIX_AS_TOKEN']
|
||||
}
|
||||
r = requests.post(url, headers=headers, data=json.dumps(room))
|
||||
|
||||
if r.status_code == 200:
|
||||
|
@ -451,13 +463,17 @@ def create_room(channel, user):
|
|||
)
|
||||
db_session.add(rr)
|
||||
db_session.commit()
|
||||
logger.debug(r.status_code)
|
||||
logger.debug(r)
|
||||
|
||||
else:
|
||||
logger.error("Unable to create room")
|
||||
logger.error(r.status_code)
|
||||
logger.error(r.text)
|
||||
abort(400)
|
||||
|
||||
def new_matrix_user(username):
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
token=app.config['MATRIX_AS_TOKEN'])
|
||||
data = {'type': 'm.login.application_service','user': app.config['MATRIX_PNUT_PREFIX'] + username}
|
||||
data = {'type': 'm.login.application_service','username': app.config['MATRIX_PNUT_PREFIX'] + username}
|
||||
try:
|
||||
matrix_api.register(content=data)
|
||||
|
||||
|
@ -466,13 +482,13 @@ def new_matrix_user(username):
|
|||
logger.warning(errmsg)
|
||||
|
||||
def on_admin_event(event):
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
token=app.config['MATRIX_AS_TOKEN'])
|
||||
logger.debug("- admin room event recieved -")
|
||||
|
||||
if event['type'] != 'm.room.message':
|
||||
return jsonify({})
|
||||
|
||||
|
||||
msg = event['content']['body'].split(' ')
|
||||
|
||||
try:
|
||||
|
@ -555,7 +571,7 @@ def cmd_admin_list():
|
|||
return text
|
||||
|
||||
def cmd_admin_link(room_id, pnut_chan_id):
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
token=app.config['MATRIX_AS_TOKEN'])
|
||||
pnutpy.api.add_authorization_token(app.config['MATRIX_PNUT_TOKEN'])
|
||||
|
||||
|
@ -568,7 +584,7 @@ def cmd_admin_link(room_id, pnut_chan_id):
|
|||
try:
|
||||
channel, meta = pnutpy.api.subscribe_channel(pnut_chan_id)
|
||||
r = matrix_api.join_room(room_id)
|
||||
|
||||
|
||||
rec = Rooms(
|
||||
room_id=room_id,
|
||||
pnut_chan=channel.id,
|
||||
|
@ -588,7 +604,7 @@ def cmd_admin_link(room_id, pnut_chan_id):
|
|||
return errmsg
|
||||
|
||||
def cmd_admin_unlink(rid):
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
token=app.config['MATRIX_AS_TOKEN'])
|
||||
pnutpy.api.add_authorization_token(app.config['MATRIX_PNUT_TOKEN'])
|
||||
|
||||
|
@ -730,20 +746,20 @@ def on_direct_message(event, user, room):
|
|||
|
||||
except pnutpy.errors.PnutAuthAPIException:
|
||||
logger.exception('-unable to post to pnut channel-')
|
||||
|
||||
|
||||
except Exception:
|
||||
logger.exception('-something bad happened here-')
|
||||
|
||||
return jsonify({})
|
||||
|
||||
def on_control_message(event):
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
||||
token=app.config['MATRIX_AS_TOKEN'])
|
||||
logger.debug("- direct room event received -")
|
||||
|
||||
if event['type'] != 'm.room.message':
|
||||
return jsonify({})
|
||||
|
||||
|
||||
msg = event['content']['body'].split(' ')
|
||||
|
||||
try:
|
||||
|
@ -839,7 +855,7 @@ def cmd_user_drop(sender=None):
|
|||
db_session.commit()
|
||||
reply = "Success! Your auth token has been removed."
|
||||
else:
|
||||
reply = "You do not appear to be registered."
|
||||
reply = "You do not appear to be registered."
|
||||
|
||||
except Exception as e:
|
||||
logging.exception('!drop')
|
||||
|
|
|
@ -70,6 +70,8 @@ def new_message(msg, meta):
|
|||
invitees=[]
|
||||
for pm_user in meta['subscribed_user_ids']:
|
||||
user = Users.query.filter(Users.pnut_user_id == pm_user).one_or_none()
|
||||
if int(pm_user) == msg.user.id:
|
||||
continue
|
||||
if user is not None:
|
||||
invitees.append(user.matrix_id)
|
||||
if len(invitees) > 0:
|
||||
|
@ -83,7 +85,7 @@ def new_message(msg, meta):
|
|||
return
|
||||
|
||||
matrix_id = matrix_id_from_pnut(msg.user.username)
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=matrix_id)
|
||||
|
||||
|
@ -92,11 +94,11 @@ def new_message(msg, meta):
|
|||
new_matrix_user(msg.user.username)
|
||||
logger.debug('-new_user-')
|
||||
profile = {'displayname': None}
|
||||
|
||||
|
||||
if profile['displayname'] != matrix_display_from_pnut(msg.user):
|
||||
set_matrix_display(msg.user)
|
||||
logger.debug('-set_display-')
|
||||
|
||||
|
||||
avatar = Avatars.query.filter(Avatars.pnut_user == msg.user.username).one_or_none()
|
||||
if avatar is None or avatar.avatar != msg.user.content.avatar_image.url:
|
||||
set_matrix_avatar(msg.user)
|
||||
|
@ -124,10 +126,10 @@ def new_message(msg, meta):
|
|||
|
||||
r = matrix_api.send_message(room.room_id, text, timestamp=ts)
|
||||
event = Events(
|
||||
event_id=r['event_id'],
|
||||
room_id=room.room_id,
|
||||
pnut_msg_id=msg.id,
|
||||
pnut_user_id=msg.user.id,
|
||||
event_id=r['event_id'],
|
||||
room_id=room.room_id,
|
||||
pnut_msg_id=msg.id,
|
||||
pnut_user_id=msg.user.id,
|
||||
pnut_chan_id=msg.channel_id,
|
||||
deleted=False)
|
||||
db_session.add(event)
|
||||
|
@ -139,7 +141,7 @@ def new_message(msg, meta):
|
|||
|
||||
def new_media(room_id, msg):
|
||||
matrix_id = matrix_id_from_pnut(msg.user.username)
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=matrix_id)
|
||||
ts = int(time.time()) * 1000
|
||||
|
@ -184,10 +186,10 @@ def new_media(room_id, msg):
|
|||
|
||||
r = matrix_api.send_content(room_id, ul['content_uri'], title, msgtype, extra_information=info, timestamp=ts)
|
||||
event = Events(
|
||||
event_id=r['event_id'],
|
||||
room_id=room_id,
|
||||
pnut_msg_id=msg.id,
|
||||
pnut_user_id=msg.user.id,
|
||||
event_id=r['event_id'],
|
||||
room_id=room_id,
|
||||
pnut_msg_id=msg.id,
|
||||
pnut_user_id=msg.user.id,
|
||||
pnut_chan_id=msg.channel_id,
|
||||
deleted=False)
|
||||
db_session.add(event)
|
||||
|
@ -195,7 +197,7 @@ def new_media(room_id, msg):
|
|||
|
||||
def delete_message(msg):
|
||||
matrix_id = matrix_id_from_pnut(msg.user.username)
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=matrix_id)
|
||||
|
||||
|
@ -227,14 +229,14 @@ def get_matrix_profile(matrix_id):
|
|||
|
||||
def set_matrix_display(user):
|
||||
matrix_id = matrix_id_from_pnut(user.username)
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=matrix_id)
|
||||
matrix_api.set_display_name(matrix_id, matrix_display_from_pnut(user))
|
||||
|
||||
def set_matrix_avatar(user):
|
||||
matrix_id = matrix_id_from_pnut(user.username)
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=matrix_id)
|
||||
|
||||
|
@ -258,24 +260,24 @@ def set_matrix_avatar(user):
|
|||
logger.exception('failed to set user avatar')
|
||||
|
||||
def new_matrix_user(username):
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'])
|
||||
data = {
|
||||
'type': 'm.login.application_service',
|
||||
'user': config['MATRIX_PNUT_PREFIX'] + username
|
||||
'username': config['MATRIX_PNUT_PREFIX'] + username
|
||||
}
|
||||
matrix_api.register(content=data)
|
||||
|
||||
def join_room(room_id, matrix_id):
|
||||
matrix_api_as = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api_as = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'])
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=matrix_id)
|
||||
|
||||
|
||||
try:
|
||||
matrix_api.join_room(room_id)
|
||||
|
||||
|
||||
except MatrixRequestError as e:
|
||||
if e.code == 403:
|
||||
matrix_api_as.invite_user(room_id, matrix_id)
|
||||
|
@ -287,7 +289,7 @@ def join_room(room_id, matrix_id):
|
|||
|
||||
def new_room(pnut_user, invitees, chan):
|
||||
dr = None
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
matrix_api = MatrixHttpApi(config['MATRIX_HOST'],
|
||||
token=config['MATRIX_AS_TOKEN'],
|
||||
identity=pnut_user)
|
||||
url = matrix_url + '/createRoom'
|
||||
|
@ -310,7 +312,7 @@ def on_message(ws, message):
|
|||
# logger.debug("on_message: " + message)
|
||||
msg = json.loads(message)
|
||||
logger.debug(msg['meta'])
|
||||
|
||||
|
||||
if 'data' in msg:
|
||||
|
||||
if 'channel_type' in msg['meta']:
|
||||
|
@ -405,7 +407,7 @@ if __name__ == '__main__':
|
|||
init_db()
|
||||
|
||||
# setup the websocket connection
|
||||
ws = websocket.WebSocketApp(ws_url, on_message=on_message,
|
||||
ws = websocket.WebSocketApp(ws_url, on_message=on_message,
|
||||
on_error=on_error, on_close=on_close)
|
||||
ws.on_open = on_open
|
||||
wst = threading.Thread(target=wsthreader(ws.run_forever))
|
||||
|
|
26
wait-for-synapse.sh
Executable file
26
wait-for-synapse.sh
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/sh
|
||||
# wait-for-synapse.sh
|
||||
#
|
||||
# based on an exmaple from https://docs.docker.com/compose/startup-order/
|
||||
#
|
||||
# command: ["/usr/src/app/wait-for-synapse.sh", "http://synapse:8008", "python", "/usr/src/app/pnut-matrix.py", "-d"]
|
||||
|
||||
set -e
|
||||
|
||||
host="$1"
|
||||
# Shift arguments with mapping:
|
||||
# - $0 => $0
|
||||
# - $1 => <discarded>
|
||||
# - $2 => $1
|
||||
# - $3 => $2
|
||||
# - ...
|
||||
# This is done for `exec "$@"` below to work correctly
|
||||
shift
|
||||
|
||||
until [ "200" -eq $(curl -s -o /dev/null --head -w "%{http_code}" ${host}/_matrix/client/versions) ]; do
|
||||
>&2 echo "synapse is unavailable - sleeping"
|
||||
sleep 3
|
||||
done
|
||||
|
||||
>&2 echo "synapse is up - executing command"
|
||||
exec "$@"
|
Loading…
Reference in a new issue