kopia lustrzana https://github.com/mate-dev/meshtastic-matrix-relay
Simplify logging, formatted with black
rodzic
11a2338332
commit
3dc1ad6119
102
main.py
102
main.py
|
@ -12,12 +12,20 @@ import certifi
|
||||||
import ssl
|
import ssl
|
||||||
import meshtastic.tcp_interface
|
import meshtastic.tcp_interface
|
||||||
import meshtastic.serial_interface
|
import meshtastic.serial_interface
|
||||||
from nio import AsyncClient, AsyncClientConfig, MatrixRoom, RoomMessageText, RoomAliasEvent, RoomMessageNotice
|
from nio import (
|
||||||
|
AsyncClient,
|
||||||
|
AsyncClientConfig,
|
||||||
|
MatrixRoom,
|
||||||
|
RoomMessageText,
|
||||||
|
RoomAliasEvent,
|
||||||
|
RoomMessageNotice,
|
||||||
|
)
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from yaml.loader import SafeLoader
|
from yaml.loader import SafeLoader
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class CustomFormatter(logging.Formatter):
|
class CustomFormatter(logging.Formatter):
|
||||||
def __init__(self, fmt=None, datefmt=None, style="%", converter=None):
|
def __init__(self, fmt=None, datefmt=None, style="%", converter=None):
|
||||||
super().__init__(fmt, datefmt, style)
|
super().__init__(fmt, datefmt, style)
|
||||||
|
@ -37,31 +45,32 @@ def utc_converter(timestamp, _):
|
||||||
return time.gmtime(timestamp)
|
return time.gmtime(timestamp)
|
||||||
|
|
||||||
|
|
||||||
bot_start_time = int(time.time() * 1000) # Timestamp when the bot starts, used to filter out old messages
|
bot_start_time = int(
|
||||||
|
time.time() * 1000
|
||||||
|
) # Timestamp when the bot starts, used to filter out old messages
|
||||||
|
|
||||||
# Load configuration
|
# Load configuration
|
||||||
with open("config.yaml", "r") as f:
|
with open("config.yaml", "r") as f:
|
||||||
relay_config = yaml.load(f, Loader=SafeLoader)
|
relay_config = yaml.load(f, Loader=SafeLoader)
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logger = logging.getLogger(name="MMRELAY")
|
logger = logging.getLogger(name="M<>M Relay")
|
||||||
log_level = getattr(logging, relay_config["logging"]["level"].upper())
|
log_level = getattr(logging, relay_config["logging"]["level"].upper())
|
||||||
show_timestamps = relay_config["logging"]["show_timestamps"]
|
|
||||||
timestamp_format = relay_config["logging"]["timestamp_format"]
|
|
||||||
|
|
||||||
if show_timestamps:
|
|
||||||
log_format = f"%(asctime)s %(levelname)s:%(name)s:%(message)s"
|
|
||||||
else:
|
|
||||||
log_format = "%(levelname)s:%(name)s:%(message)s"
|
|
||||||
|
|
||||||
logger.setLevel(getattr(logging, relay_config["logging"]["level"].upper()))
|
logger.setLevel(log_level)
|
||||||
logger.propagate = False # Add this line to prevent double logging
|
logger.propagate = False # Add this line to prevent double logging
|
||||||
|
|
||||||
formatter = CustomFormatter(log_format, datefmt=timestamp_format, converter=utc_converter)
|
formatter = CustomFormatter(
|
||||||
|
fmt=f"%(asctime)s %(levelname)s:%(name)s:%(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
converter=utc_converter,
|
||||||
|
)
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
# Initialize SQLite database
|
# Initialize SQLite database
|
||||||
def initialize_database():
|
def initialize_database():
|
||||||
with sqlite3.connect("meshtastic.sqlite") as conn:
|
with sqlite3.connect("meshtastic.sqlite") as conn:
|
||||||
|
@ -109,7 +118,9 @@ async def join_matrix_room(matrix_client, room_id_or_alias: str) -> None:
|
||||||
if room_id_or_alias.startswith("#"):
|
if room_id_or_alias.startswith("#"):
|
||||||
response = await matrix_client.resolve_room_alias(room_id_or_alias)
|
response = await matrix_client.resolve_room_alias(room_id_or_alias)
|
||||||
if not response.room_id:
|
if not response.room_id:
|
||||||
logger.error(f"Failed to resolve room alias '{room_id_or_alias}': {response.message}")
|
logger.error(
|
||||||
|
f"Failed to resolve room alias '{room_id_or_alias}': {response.message}"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
room_id = response.room_id
|
room_id = response.room_id
|
||||||
else:
|
else:
|
||||||
|
@ -117,15 +128,18 @@ async def join_matrix_room(matrix_client, room_id_or_alias: str) -> None:
|
||||||
|
|
||||||
if room_id not in matrix_client.rooms:
|
if room_id not in matrix_client.rooms:
|
||||||
response = await matrix_client.join(room_id)
|
response = await matrix_client.join(room_id)
|
||||||
if response and hasattr(response, 'room_id'):
|
if response and hasattr(response, "room_id"):
|
||||||
logger.info(f"Joined room '{room_id_or_alias}' successfully")
|
logger.info(f"Joined room '{room_id_or_alias}' successfully")
|
||||||
else:
|
else:
|
||||||
logger.error(f"Failed to join room '{room_id_or_alias}': {response.message}")
|
logger.error(
|
||||||
|
f"Failed to join room '{room_id_or_alias}': {response.message}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Bot is already in room '{room_id_or_alias}'")
|
logger.debug(f"Bot is already in room '{room_id_or_alias}'")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error joining room '{room_id_or_alias}': {e}")
|
logger.error(f"Error joining room '{room_id_or_alias}': {e}")
|
||||||
|
|
||||||
|
|
||||||
# Initialize Meshtastic interface
|
# Initialize Meshtastic interface
|
||||||
connection_type = relay_config["meshtastic"]["connection_type"]
|
connection_type = relay_config["meshtastic"]["connection_type"]
|
||||||
if connection_type == "serial":
|
if connection_type == "serial":
|
||||||
|
@ -145,6 +159,7 @@ matrix_access_token = relay_config["matrix"]["access_token"]
|
||||||
bot_user_id = relay_config["matrix"]["bot_user_id"]
|
bot_user_id = relay_config["matrix"]["bot_user_id"]
|
||||||
matrix_rooms: List[dict] = relay_config["matrix_rooms"]
|
matrix_rooms: List[dict] = relay_config["matrix_rooms"]
|
||||||
|
|
||||||
|
|
||||||
# Send message to the Matrix room
|
# Send message to the Matrix room
|
||||||
async def matrix_relay(matrix_client, room_id, message, longname, meshnet_name):
|
async def matrix_relay(matrix_client, room_id, message, longname, meshnet_name):
|
||||||
try:
|
try:
|
||||||
|
@ -197,18 +212,28 @@ def on_meshtastic_message(packet, loop=None):
|
||||||
logger.debug(f"Skipping message from unmapped channel {channel}")
|
logger.debug(f"Skipping message from unmapped channel {channel}")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"Processing inbound radio message from {sender} on channel {channel}")
|
logger.info(
|
||||||
|
f"Processing inbound radio message from {sender} on channel {channel}"
|
||||||
|
)
|
||||||
|
|
||||||
longname = get_longname(sender) or sender
|
longname = get_longname(sender) or sender
|
||||||
meshnet_name = relay_config["meshtastic"]["meshnet_name"]
|
meshnet_name = relay_config["meshtastic"]["meshnet_name"]
|
||||||
|
|
||||||
formatted_message = f"[{longname}/{meshnet_name}]: {text}"
|
formatted_message = f"[{longname}/{meshnet_name}]: {text}"
|
||||||
logger.info(f"Relaying Meshtastic message from {longname} to Matrix: {formatted_message}")
|
logger.info(
|
||||||
|
f"Relaying Meshtastic message from {longname} to Matrix: {formatted_message}"
|
||||||
|
)
|
||||||
|
|
||||||
for room in matrix_rooms:
|
for room in matrix_rooms:
|
||||||
if room["meshtastic_channel"] == channel:
|
if room["meshtastic_channel"] == channel:
|
||||||
asyncio.run_coroutine_threadsafe(
|
asyncio.run_coroutine_threadsafe(
|
||||||
matrix_relay(matrix_client, room["id"], formatted_message, longname, meshnet_name),
|
matrix_relay(
|
||||||
|
matrix_client,
|
||||||
|
room["id"],
|
||||||
|
formatted_message,
|
||||||
|
longname,
|
||||||
|
meshnet_name,
|
||||||
|
),
|
||||||
loop=loop,
|
loop=loop,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -223,8 +248,9 @@ def on_meshtastic_message(packet, loop=None):
|
||||||
logger.debug(f"Ignoring Unknown packet")
|
logger.debug(f"Ignoring Unknown packet")
|
||||||
|
|
||||||
|
|
||||||
|
def truncate_message(
|
||||||
def truncate_message(text, max_bytes=234): #234 is the maximum that we can run without an error. Trying it for awhile, otherwise lower this to 230 or less.
|
text, max_bytes=234
|
||||||
|
): # 234 is the maximum that we can run without an error. Trying it for awhile, otherwise lower this to 230 or less.
|
||||||
"""
|
"""
|
||||||
Truncate the given text to fit within the specified byte size.
|
Truncate the given text to fit within the specified byte size.
|
||||||
|
|
||||||
|
@ -232,14 +258,14 @@ def truncate_message(text, max_bytes=234): #234 is the maximum that we can run
|
||||||
:param max_bytes: The maximum allowed byte size for the truncated text.
|
:param max_bytes: The maximum allowed byte size for the truncated text.
|
||||||
:return: The truncated text.
|
:return: The truncated text.
|
||||||
"""
|
"""
|
||||||
truncated_text = text.encode('utf-8')[:max_bytes].decode('utf-8', 'ignore')
|
truncated_text = text.encode("utf-8")[:max_bytes].decode("utf-8", "ignore")
|
||||||
return truncated_text
|
return truncated_text
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Callback for new messages in Matrix room
|
# Callback for new messages in Matrix room
|
||||||
async def on_room_message(room: MatrixRoom, event: Union[RoomMessageText, RoomMessageNotice]) -> None:
|
async def on_room_message(
|
||||||
|
room: MatrixRoom, event: Union[RoomMessageText, RoomMessageNotice]
|
||||||
|
) -> None:
|
||||||
full_display_name = "Unknown user"
|
full_display_name = "Unknown user"
|
||||||
if event.sender != bot_user_id:
|
if event.sender != bot_user_id:
|
||||||
message_timestamp = event.server_timestamp
|
message_timestamp = event.server_timestamp
|
||||||
|
@ -247,8 +273,8 @@ async def on_room_message(room: MatrixRoom, event: Union[RoomMessageText, RoomMe
|
||||||
if message_timestamp > bot_start_time:
|
if message_timestamp > bot_start_time:
|
||||||
text = event.body.strip()
|
text = event.body.strip()
|
||||||
|
|
||||||
longname = event.source['content'].get("meshtastic_longname")
|
longname = event.source["content"].get("meshtastic_longname")
|
||||||
meshnet_name = event.source['content'].get("meshtastic_meshnet")
|
meshnet_name = event.source["content"].get("meshtastic_meshnet")
|
||||||
local_meshnet_name = relay_config["meshtastic"]["meshnet_name"]
|
local_meshnet_name = relay_config["meshtastic"]["meshnet_name"]
|
||||||
|
|
||||||
if longname and meshnet_name:
|
if longname and meshnet_name:
|
||||||
|
@ -262,11 +288,15 @@ async def on_room_message(room: MatrixRoom, event: Union[RoomMessageText, RoomMe
|
||||||
logger.info(f"Processing message from local meshnet: {text}")
|
logger.info(f"Processing message from local meshnet: {text}")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
display_name_response = await matrix_client.get_displayname(event.sender)
|
display_name_response = await matrix_client.get_displayname(
|
||||||
|
event.sender
|
||||||
|
)
|
||||||
full_display_name = display_name_response.displayname or event.sender
|
full_display_name = display_name_response.displayname or event.sender
|
||||||
short_display_name = full_display_name[:5]
|
short_display_name = full_display_name[:5]
|
||||||
prefix = f"{short_display_name}[M]: "
|
prefix = f"{short_display_name}[M]: "
|
||||||
logger.info(f"Processing matrix message from [{full_display_name}]: {text}")
|
logger.info(
|
||||||
|
f"Processing matrix message from [{full_display_name}]: {text}"
|
||||||
|
)
|
||||||
|
|
||||||
text = truncate_message(text)
|
text = truncate_message(text)
|
||||||
full_message = f"{prefix}{text}"
|
full_message = f"{prefix}{text}"
|
||||||
|
@ -281,13 +311,16 @@ async def on_room_message(room: MatrixRoom, event: Union[RoomMessageText, RoomMe
|
||||||
meshtastic_channel = room_config["meshtastic_channel"]
|
meshtastic_channel = room_config["meshtastic_channel"]
|
||||||
|
|
||||||
if relay_config["meshtastic"]["broadcast_enabled"]:
|
if relay_config["meshtastic"]["broadcast_enabled"]:
|
||||||
logger.info(f"Sending radio message from {full_display_name} to radio broadcast")
|
logger.info(
|
||||||
|
f"Sending radio message from {full_display_name} to radio broadcast"
|
||||||
|
)
|
||||||
meshtastic_interface.sendText(
|
meshtastic_interface.sendText(
|
||||||
text=full_message, channelIndex=meshtastic_channel
|
text=full_message, channelIndex=meshtastic_channel
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.debug(f"Broadcast not supported: Message from {full_display_name} dropped.")
|
logger.debug(
|
||||||
|
f"Broadcast not supported: Message from {full_display_name} dropped."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
@ -301,7 +334,9 @@ async def main():
|
||||||
|
|
||||||
# Initialize the Matrix client with custom SSL context
|
# Initialize the Matrix client with custom SSL context
|
||||||
config = AsyncClientConfig(encryption_enabled=False)
|
config = AsyncClientConfig(encryption_enabled=False)
|
||||||
matrix_client = AsyncClient(matrix_homeserver, bot_user_id, config=config, ssl=ssl_context)
|
matrix_client = AsyncClient(
|
||||||
|
matrix_homeserver, bot_user_id, config=config, ssl=ssl_context
|
||||||
|
)
|
||||||
matrix_client.access_token = matrix_access_token
|
matrix_client.access_token = matrix_access_token
|
||||||
|
|
||||||
logger.info("Connecting to Matrix server...")
|
logger.info("Connecting to Matrix server...")
|
||||||
|
@ -324,7 +359,9 @@ async def main():
|
||||||
|
|
||||||
# Register the message callback
|
# Register the message callback
|
||||||
logger.info(f"Listening for inbound matrix messages ...")
|
logger.info(f"Listening for inbound matrix messages ...")
|
||||||
matrix_client.add_event_callback(on_room_message, (RoomMessageText, RoomMessageNotice))
|
matrix_client.add_event_callback(
|
||||||
|
on_room_message, (RoomMessageText, RoomMessageNotice)
|
||||||
|
)
|
||||||
|
|
||||||
# Start the Matrix client
|
# Start the Matrix client
|
||||||
while True:
|
while True:
|
||||||
|
@ -340,4 +377,5 @@ async def main():
|
||||||
|
|
||||||
await asyncio.sleep(60) # Update longnames every 60 seconds
|
await asyncio.sleep(60) # Update longnames every 60 seconds
|
||||||
|
|
||||||
|
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
Ładowanie…
Reference in New Issue