Matrix
是一个开源的聊天协议,和 XMPP
一样的联邦制设计也保证了像电子邮件一样的通信便捷。
好的地方很多,但最明显坏处就是推送了,对于即时通信来说,没有推送是非常大的不便……虽然搭建 Matrix
的应用 Synapse
配置文件启用邮箱的话可以启用邮件通知,不过鉴于邮件时效性也不是很高,所以需要个其他的推送手段。(谷歌市场下的 Element
附带了谷歌的消息推送,不过很遗憾在国内处于残废状态。)
所幸 Synapse
自带的 API 可以定义推送规则,定义后帐号收到消息,服务器就可以将消息以 JSON
的形式推送到自定义的地址,来个曲线救国。
Gotify 推送用到的工具为 Gotify
,特点是使用 GO
编写,高效快速,不过缺点就是安卓得常驻后台接收推送,不过至少要比腾讯全家桶吃的电要少。
下载 去 Releases 处找到对应版本的下载链接,解压到/opt/gotify/
。
1 unzip gotify-file -d /opt/gotify/
然后下载配置样板。
1 2 mkdir /etc/gotifywget -O /etc/gotify/config.yml https://raw.githubusercontent.com/gotify/server/master/config.example.yml
编辑 /etc/gotify/config.yml
。将 port
改为没有占用的端口, name
和 pass
是管理员的用户名和密码。
Nginx 反代 在 nginx
的新建一个虚拟主机并在配置文件增加以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 location / { proxy_pass http://localhost:8080; rewrite ^/gotify(/.*) $1 break ; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection "upgrade" ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header X-Forwarded-Proto http; proxy_redirect http:// $scheme ://; }
proxy_set_header Connection “upgrade”; 这一栏不要漏了……要不然开启 WS 时会报错……
设置服务 新建并编辑 /etc/systemd/system/gotify.service
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [Unit] Description=gotify service After=network.target Wants=network.target [Service] Type=simple PIDFile=/run/gotify.pid WorkingDirectory=/opt/gotify ExecStart=/opt/gotify/gotify-file RestartPreventExitStatus=23 Restart=always RestartSec=10s [Install] WantedBy=multi-user.target
然后开启服务并设置成开机启动。
1 2 systemctl start gotify systemctl enable gotify
没有报错的话就可以打开 Nginx
里所设置的域名进入管理页面了,(改密码)之后就可以新建应用了,拿到推送令牌,就可以向服务器设置推送规则了。
设置推送 在官方文档里,通过 /_matrix/client/v3/pushers/set API
就可以设置推送规则了,通过 curl
,或者其他方式向服务器对应地址发送 POST
请求即可使用。
1 curl 'https://server_url/_matrix/client/r0/pushers/set' -H 'Authorization: Bearer access_token' -H 'Content-Type: application/json' -X POST -d '{"lang": "en","kind": "http","app_display_name": "Gotify","device_display_name": "Gotify","pushkey": "Gotify-PushKey","app_id": "zh.xxx.gotify","data": {"url": "https://Push_url/_matrix/push/v1/notify","format": "full_event"}}'
详细说明请查阅官方 文档 , 其中 access_token
可以在应用里拿到,pushkey
设置成 Gotify-PushKey
的样子是为了后续处理方便……现阶段推送地址必须包含 /_matrix/push/v1/notify
路径,否则会报错,所以不得不进行额外处理了……
接收推送 服务器设置推送规则之后就会向对应地址发送数据了,因为推送地址现阶段必须包含 /_matrix/push/v1/notify
路径,所以不得不再设置一个接收服务然后进行处理了。
接下来使用 python
的 flask
框架简单搭一个服务,首先先安装环境。
1 2 pip3 install flask pip3 install flask-apscheduler
然后是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 import jsonimport requestsimport datetimefrom flask import Flask, jsonify, requestfrom flask_apscheduler import APSchedulerapp = Flask(__name__) scheduler = APScheduler() scheduler.start() def push_notification (push_data ): if push_data["push_way" ] == 'Gotify' : resp = requests.post(f'https://push.xxx.xxx/message?token={push_data["push_token" ]} ' , json={ "message" : f'「{push_data["sender" ]} 」发送了消息给你。' , "priority" : 8 , "title" : "新消息!" }) print (resp) return if push_data["push_way" ] == 'Bary' : try : response = requests.post( url="https://api.day.app/push" , headers={ "Content-Type" : "application/json; charset=utf-8" , }, data=json.dumps({ "body" : f'「{push_data["sender" ]} 」发送了消息给你。' , "device_key" : push_data["push_token" ], "title" : "新消息!" , "category" : "category" , "sound" : "minuet.caf" , }) ) print ('Response HTTP Status Code: {status_code}' .format ( status_code=response.status_code)) print ('Response HTTP Response Body: {content}' .format ( content=response.content)) except requests.exceptions.RequestException: print ('HTTP Request failed' ) return @app.route('/_matrix/push/v1/notify' , methods=['POST' ] ) def hello_world (): push_data = {} event = None try : event = request.json print (event) except : return jsonify(success=False ) if event["notification" ]["type" ] == 'm.room.message' or event["notification" ]["type" ] == 'm.room.encrypted' : app_id = event["notification" ]["devices" ][0 ]["app_id" ] push_data["push_way" ], push_data["push_token" ] = event["notification" ]["devices" ][0 ]["pushkey" ].split('-' ) push_data["sender" ] = event["notification" ]["sender_display_name" ] scheduler.add_job( func=push_notification, args=(push_data,), next_run_time=datetime.datetime.now() + datetime.timedelta(seconds=25 ), id =app_id, replace_existing=True , ) print ('✅add push job!' ) if event["notification" ]["type" ] == None and event["notification" ]["id" ] == '' : app_id = event["notification" ]["devices" ][0 ]["app_id" ] try : scheduler.remove_job(id =app_id) except : pass print ('✅remove push!' ) return jsonify(success=True )
然后用命令 FLASK_ENV=development FLASK_RUN_PORT=4567 FLASK_APP=webhook.py flask run
就可启用。再然后再用 Nginx
反代一下 4567
端口就行了,因为 Synapse
默认禁止本机访问,为了规范一点就另外开个站点接收请求吧……
基本逻辑就是接收 JSON
拿到发送者名称和推送令牌,判断是哪项服务,然后等待三十秒推送到对应的应用令牌上去,期间要是判断用户已经阅读了消息,就移除推送作业。
到此无意外的话,应该就能正常推送了,麻烦……希望以后能够有更友好便捷的方式吧。
参考
在树莓派上部署消息推送软件Gotify