2019-09-25 14:26:29 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import asyncio
|
2020-08-10 00:02:07 +02:00
|
|
|
import logging
|
2020-05-31 21:20:54 +02:00
|
|
|
import sys
|
2022-07-04 01:03:24 +02:00
|
|
|
from asyncio import TimeoutError
|
2020-02-24 23:13:28 +01:00
|
|
|
from time import sleep
|
2020-08-10 00:02:07 +02:00
|
|
|
|
2022-07-06 00:54:13 +02:00
|
|
|
from aiohttp import ClientConnectionError, ServerDisconnectedError
|
2022-07-08 21:11:25 +02:00
|
|
|
from diskcache import Cache
|
2019-09-25 14:26:29 +02:00
|
|
|
from nio import (
|
|
|
|
AsyncClient,
|
|
|
|
AsyncClientConfig,
|
2020-05-05 01:10:18 +02:00
|
|
|
InviteMemberEvent,
|
2020-02-24 23:13:28 +01:00
|
|
|
LocalProtocolError,
|
2020-08-10 00:02:07 +02:00
|
|
|
LoginError,
|
2021-01-10 03:58:39 +01:00
|
|
|
MegolmEvent,
|
2020-08-10 00:02:07 +02:00
|
|
|
RoomMessageText,
|
2021-01-04 05:47:27 +01:00
|
|
|
UnknownEvent,
|
2020-02-24 23:13:28 +01:00
|
|
|
)
|
2020-08-10 00:02:07 +02:00
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
from matrix_alertbot.alertmanager import AlertmanagerClient
|
2022-07-08 21:11:25 +02:00
|
|
|
from matrix_alertbot.callback import Callbacks
|
2022-06-13 20:55:01 +02:00
|
|
|
from matrix_alertbot.config import Config
|
2022-07-04 01:03:24 +02:00
|
|
|
from matrix_alertbot.webhook import Webhook
|
2019-09-25 14:26:29 +02:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-07-08 21:11:25 +02:00
|
|
|
def create_matrix_client(config: Config) -> AsyncClient:
|
2022-07-04 01:03:24 +02:00
|
|
|
# Configuration options for the AsyncClient
|
|
|
|
client_config = AsyncClientConfig(
|
|
|
|
max_limit_exceeded=0,
|
|
|
|
max_timeouts=0,
|
|
|
|
store_sync_tokens=True,
|
|
|
|
encryption_enabled=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Initialize the matrix client
|
|
|
|
client = AsyncClient(
|
|
|
|
config.homeserver_url,
|
|
|
|
config.user_id,
|
|
|
|
device_id=config.device_id,
|
2022-07-09 12:22:05 +02:00
|
|
|
store_path=config.store_dir,
|
2022-07-04 01:03:24 +02:00
|
|
|
config=client_config,
|
|
|
|
)
|
|
|
|
|
|
|
|
if config.user_token:
|
|
|
|
client.access_token = config.user_token
|
|
|
|
client.user_id = config.user_id
|
|
|
|
|
2022-07-08 21:11:25 +02:00
|
|
|
return client
|
|
|
|
|
|
|
|
|
2022-07-08 23:04:04 +02:00
|
|
|
async def start_matrix_client(
|
|
|
|
client: AsyncClient, cache: Cache, config: Config
|
|
|
|
) -> bool:
|
|
|
|
# Keep trying to reconnect on failure (with some time in-between)
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
if config.user_token:
|
|
|
|
# Use token to log in
|
|
|
|
client.load_store()
|
|
|
|
|
|
|
|
# Sync encryption keys with the server
|
|
|
|
if client.should_upload_keys:
|
|
|
|
await client.keys_upload()
|
|
|
|
else:
|
|
|
|
# Try to login with the configured username/password
|
2022-07-08 21:11:25 +02:00
|
|
|
try:
|
2022-07-08 23:04:04 +02:00
|
|
|
login_response = await client.login(
|
|
|
|
password=config.user_password,
|
|
|
|
device_name=config.device_name,
|
2022-07-08 21:11:25 +02:00
|
|
|
)
|
|
|
|
|
2022-07-08 23:04:04 +02:00
|
|
|
# Check if login failed
|
|
|
|
if type(login_response) == LoginError:
|
|
|
|
logger.error("Failed to login: %s", login_response.message)
|
|
|
|
return False
|
|
|
|
except LocalProtocolError as e:
|
|
|
|
# There's an edge case here where the user hasn't installed the correct C
|
|
|
|
# dependencies. In that case, a LocalProtocolError is raised on login.
|
|
|
|
logger.fatal(
|
|
|
|
"Failed to login. Have you installed the correct dependencies? "
|
|
|
|
"https://github.com/poljar/matrix-nio#installation "
|
|
|
|
"Error: %s",
|
|
|
|
e,
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Login succeeded!
|
2022-07-08 21:11:25 +02:00
|
|
|
|
2022-07-08 23:04:04 +02:00
|
|
|
logger.info(f"Logged in as {config.user_id}")
|
|
|
|
await client.sync_forever(timeout=30000, full_state=True)
|
|
|
|
except (ClientConnectionError, ServerDisconnectedError, TimeoutError):
|
|
|
|
logger.warning("Unable to connect to homeserver, retrying in 15s...")
|
2022-07-08 21:11:25 +02:00
|
|
|
|
2022-07-08 23:04:04 +02:00
|
|
|
# Sleep so we don't bombard the server with login requests
|
2022-07-08 23:28:23 +02:00
|
|
|
await asyncio.sleep(15)
|
2022-07-08 23:04:04 +02:00
|
|
|
finally:
|
2022-07-09 00:02:34 +02:00
|
|
|
await client.close()
|
2022-07-08 21:11:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
|
|
"""The first function that is run when starting the bot"""
|
|
|
|
|
|
|
|
# Read user-configured options from a config file.
|
|
|
|
# A different config file path can be specified as the first command line argument
|
|
|
|
if len(sys.argv) > 1:
|
|
|
|
config_path = sys.argv[1]
|
|
|
|
else:
|
|
|
|
config_path = "config.yaml"
|
|
|
|
|
|
|
|
# Read the parsed config file and create a Config object
|
|
|
|
config = Config(config_path)
|
2022-07-04 01:03:24 +02:00
|
|
|
|
2022-07-08 23:04:04 +02:00
|
|
|
client = create_matrix_client(config)
|
|
|
|
|
2022-07-08 21:11:25 +02:00
|
|
|
# Configure the cache
|
|
|
|
cache = Cache(config.cache_dir)
|
2022-07-04 01:03:24 +02:00
|
|
|
|
2022-07-08 23:04:04 +02:00
|
|
|
# Configure Alertmanager client
|
|
|
|
alertmanager = AlertmanagerClient(config.alertmanager_url, cache)
|
|
|
|
|
|
|
|
# Set up event callbacks
|
|
|
|
callbacks = Callbacks(client, alertmanager, cache, config)
|
|
|
|
client.add_event_callback(callbacks.message, (RoomMessageText,))
|
|
|
|
client.add_event_callback(
|
|
|
|
callbacks.invite_event_filtered_callback, (InviteMemberEvent,)
|
|
|
|
)
|
|
|
|
client.add_event_callback(callbacks.decryption_failure, (MegolmEvent,))
|
|
|
|
client.add_event_callback(callbacks.unknown, (UnknownEvent,))
|
|
|
|
|
|
|
|
# Configure webhook server
|
|
|
|
webhook_server = Webhook(client, cache, config)
|
|
|
|
|
2022-07-04 01:03:24 +02:00
|
|
|
loop = asyncio.get_event_loop()
|
2022-07-08 23:04:04 +02:00
|
|
|
loop.create_task(webhook_server.start())
|
|
|
|
loop.create_task(start_matrix_client(client, cache, config))
|
2022-07-04 01:03:24 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
loop.run_forever()
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(e)
|
2022-07-08 23:04:04 +02:00
|
|
|
finally:
|
|
|
|
loop.run_until_complete(webhook_server.close())
|
|
|
|
loop.run_until_complete(alertmanager.close())
|
|
|
|
loop.run_until_complete(client.close())
|