Initial commit
This commit is contained in:
commit
d6b1062fe8
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.mbp
|
||||||
|
*.zip
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020 Tulir Asokan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# echo
|
||||||
|
A simple [maubot](https://github.com/maubot/maubot) that displays everyone's homeserver software version.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
* `!shame` - Reply with a list of homeservers and their respective running versions.
|
9
base-config.yaml
Normal file
9
base-config.yaml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# The https://github.com/matrix-org/matrix-federation-tester instance to use.
|
||||||
|
# {server} is replaced with the server name.
|
||||||
|
federation_tester: https://matrix.org/federationtester/api/report?server_name={server}
|
||||||
|
#
|
||||||
|
# Instances to ignore as they might be permanently erroring and causing slow responses
|
||||||
|
# dead_servers:
|
||||||
|
# - host1.example.com
|
||||||
|
# - host2.example.com
|
||||||
|
# - host.example.org
|
8
maubot.yaml
Normal file
8
maubot.yaml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
maubot: 0.1.0
|
||||||
|
id: sh.wallace.matrix.shameotron
|
||||||
|
version: 0.0.2
|
||||||
|
license: MIT
|
||||||
|
modules:
|
||||||
|
- shameotron
|
||||||
|
main_class: ShameOTron
|
||||||
|
database: false
|
141
shameotron.py
Normal file
141
shameotron.py
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
"""
|
||||||
|
[Maubot](https://mau.dev/maubot/maubot) plugin to shame room members into
|
||||||
|
upgrading their Matrix homeservers to the latest version.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
from typing import Dict, List, Type
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper
|
||||||
|
from mautrix.types import TextMessageEventContent, MessageType, Format, \
|
||||||
|
RelatesTo, RelationType, EventID, RoomID, UserID
|
||||||
|
from mautrix.util import markdown
|
||||||
|
|
||||||
|
from maubot import Plugin, MessageEvent
|
||||||
|
from maubot.handlers import command
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseProxyConfig):
|
||||||
|
"""
|
||||||
|
Config class
|
||||||
|
"""
|
||||||
|
def do_update(self, helper: ConfigUpdateHelper) -> None:
|
||||||
|
"""
|
||||||
|
Class method to update the config
|
||||||
|
"""
|
||||||
|
helper.copy('federation_tester')
|
||||||
|
helper.copy('dead_servers')
|
||||||
|
|
||||||
|
|
||||||
|
class ShameOTron(Plugin):
|
||||||
|
"""
|
||||||
|
Main class for the Shame-o-Tron
|
||||||
|
"""
|
||||||
|
async def start(self) -> None:
|
||||||
|
"""
|
||||||
|
Class method for plugin startup
|
||||||
|
"""
|
||||||
|
self.on_external_config_update()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_config_class(cls) -> Type[Config]:
|
||||||
|
"""
|
||||||
|
Class method for getting the config
|
||||||
|
"""
|
||||||
|
return Config
|
||||||
|
|
||||||
|
|
||||||
|
async def _edit(self, room_id: RoomID, event_id: EventID, text: str) -> None:
|
||||||
|
"""
|
||||||
|
Class method to update an existing message event
|
||||||
|
"""
|
||||||
|
content = TextMessageEventContent(msgtype=MessageType.NOTICE, body=text, format=Format.HTML,
|
||||||
|
formatted_body=markdown.render(text))
|
||||||
|
content.set_edit(event_id)
|
||||||
|
await self.client.send_message(room_id, content)
|
||||||
|
|
||||||
|
|
||||||
|
async def _load_members(self, room_id: RoomID) -> Dict[str, List[UserID]]:
|
||||||
|
"""
|
||||||
|
Class method to return the servers and room members
|
||||||
|
"""
|
||||||
|
users = await self.client.get_joined_members(room_id)
|
||||||
|
servers: Dict[str, List[UserID]] = {}
|
||||||
|
for user in users:
|
||||||
|
_, server = self.client.parse_user_id(user)
|
||||||
|
servers.setdefault(server, []).append(user)
|
||||||
|
return servers
|
||||||
|
|
||||||
|
|
||||||
|
async def query_homeserver_version(self, host):
|
||||||
|
"""
|
||||||
|
Function to query the Federation Tester to retrieve the running version
|
||||||
|
for a server
|
||||||
|
|
||||||
|
host: (str) Server to get version for
|
||||||
|
|
||||||
|
Returns: (str) Version string of the server
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
req = requests.get(
|
||||||
|
self.config["federation_tester"].format(server=host),
|
||||||
|
timeout=10000
|
||||||
|
)
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
return '[TIMEOUT]'
|
||||||
|
|
||||||
|
data = json.loads(req.text)
|
||||||
|
|
||||||
|
if not data['FederationOK']:
|
||||||
|
return '[OFFLINE]'
|
||||||
|
|
||||||
|
try:
|
||||||
|
return data['Version']['version']
|
||||||
|
except KeyError:
|
||||||
|
return '[ERROR]'
|
||||||
|
|
||||||
|
|
||||||
|
@command.new('shame', help='Show versions of all homeservers in the room')
|
||||||
|
async def shame_handler(self, evt: MessageEvent) -> None:
|
||||||
|
"""
|
||||||
|
Class method to handle the `!shame` command
|
||||||
|
"""
|
||||||
|
event_id = await evt.reply('Loading member list...')
|
||||||
|
member_servers = await self._load_members(evt.room_id)
|
||||||
|
|
||||||
|
# Filter out the "dead servers"
|
||||||
|
dead_servers = self.config['dead_servers']
|
||||||
|
if dead_servers:
|
||||||
|
# Return a unique list
|
||||||
|
member_servers = sorted(
|
||||||
|
list(
|
||||||
|
set(member_servers.keys() - set(dead_servers))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._edit(
|
||||||
|
evt.room_id,
|
||||||
|
event_id,
|
||||||
|
'Member list loaded, fetching versions... please wait...'
|
||||||
|
)
|
||||||
|
|
||||||
|
versions = []
|
||||||
|
for host in member_servers:
|
||||||
|
versions.append(
|
||||||
|
(host, await self.query_homeserver_version(host))
|
||||||
|
)
|
||||||
|
|
||||||
|
await self._edit(
|
||||||
|
evt.room_id,
|
||||||
|
event_id,
|
||||||
|
(
|
||||||
|
'#### Homeserver versions\n'
|
||||||
|
+ '\n'.join(
|
||||||
|
f'* {host}: [{version}]({self.config["federation_tester"].format(server=host)})'
|
||||||
|
for host, version in versions
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
Loading…
Reference in a new issue