This commit is contained in:
DomNomNom 2025-02-15 22:42:38 +13:00
parent bea22dfa70
commit 81c98e18c0
4 changed files with 160 additions and 133 deletions

View File

@ -1,71 +1,38 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio import asyncio
import getpass import getpass
import json import json
import os import os
import sys import sys
import aiofiles import aiofiles
from nio import AsyncClient, LoginResponse from nio import AsyncClient, LoginResponse
CONFIG_FILE = "credentials.json" CONFIG_FILE = "credentials.json"
# Check out main() below to see how it's done. # Check out main() below to see how it's done.
def write_details_to_disk(resp: LoginResponse, homeserver) -> None: def write_details_to_disk(resp: LoginResponse, homeserver) -> None:
"""Writes the required login details to disk so we can log in later without """Writes the required login details to disk so we can log in later without
using a password. using a password.
Arguments: Arguments:
resp {LoginResponse} -- the successful client login response. resp {LoginResponse} -- the successful client login response.
homeserver -- URL of homeserver, e.g. "https://matrix.example.org" homeserver -- URL of homeserver, e.g. "https://matrix.example.org"
""" """
# open the config file in write-mode # open the config file in write-mode
with open(CONFIG_FILE, "w") as f: with open(CONFIG_FILE, "w") as f:
# write the login details to disk # write the login details to disk
json.dump( json.dump(
{ {
"homeserver": homeserver, # e.g. "https://matrix.example.org" "homeserver": homeserver, # e.g. "https://matrix.example.org"
"user_id": resp.user_id, # e.g. "@user:example.org" "user_id": resp.user_id, # e.g. "@user:example.org"
"device_id": resp.device_id, # device ID, 10 uppercase letters "device_id": resp.device_id, # device ID, 10 uppercase letters
"access_token": resp.access_token, # cryptogr. access token "access_token": resp.access_token, # cryptogr. access token
}, },
f, f,
) )
async def main() -> None: async def main() -> None:
# If there are no previously-saved credentials, we'll use the password # If there are no previously-saved credentials, we'll use the password
@ -75,114 +42,53 @@ async def main() -> None:
print( print(
"First time use. Did not find credential file. Asking for " "First time use. Did not find credential file. Asking for "
"homeserver, user, and password to create credential file." "homeserver, user, and password to create credential file."
) )
# homeserver = "https://matrix.example.org" # homeserver = "https://matrix.example.org"
# homeserver = input(f"Enter your homeserver URL: [{homeserver}] ") # homeserver = input(f"Enter your homeserver URL: [{homeserver}] ")
# if not (homeserver.startswith("https://") or homeserver.startswith("http://")): # if not (homeserver.startswith("https://") or homeserver.startswith("http://")):
# homeserver = "https://" + homeserver # homeserver = "https://" + homeserver
# user_id = "@user:example.org" # user_id = "@user:example.org"
# user_id = input(f"Enter your full user ID: [{user_id}] ") # user_id = input(f"Enter your full user ID: [{user_id}] ")
# device_name = "matrix-nio" # device_name = "matrix-nio"
# device_name = input(f"Choose a name for this device: [{device_name}] ") # device_name = input(f"Choose a name for this device: [{device_name}] ")
# client = AsyncClient(homeserver, user_id) # client = AsyncClient(homeserver, user_id)
# pw = getpass.getpass() # pw = getpass.getpass()
homeserver = "https://matrix.domnomnom.com" homeserver = "https://matrix.domnomnom.com"
user_id = "@getbot:matrix.domnomnom.com" user_id = "@getbot:matrix.domnomnom.com"
room_id = "!sZpfYzLsRbnIOKJlPH:matrix.domnomnom.com" room_id = "!sZpfYzLsRbnIOKJlPH:matrix.domnomnom.com"
client = AsyncClient(homeserver, user_id) client = AsyncClient(homeserver, user_id)
# client.add_event_callback(message_callback, RoomMessageText) # client.add_event_callback(message_callback, RoomMessageText)
resp = await client.login(password) resp = await client.login(password)
# check that we logged in successfully # check that we logged in successfully
if isinstance(resp, LoginResponse): if isinstance(resp, LoginResponse):
write_details_to_disk(resp, homeserver) write_details_to_disk(resp, homeserver)
else: else:
print(f'homeserver = "{homeserver}"; user = "{user_id}"') print(f'homeserver = "{homeserver}"; user = "{user_id}"')
print(f"Failed to log in: {resp}") print(f"Failed to log in: {resp}")
sys.exit(1) sys.exit(1)
print( print(
"Logged in using a password. Credentials were stored.", "Logged in using a password. Credentials were stored.",
"Try running the script again to login with credentials.", "Try running the script again to login with credentials.",
) )
# Otherwise the config file exists, so we'll use the stored credentials # Otherwise the config file exists, so we'll use the stored credentials
else: else:
# open the file in read-only mode # open the file in read-only mode
async with aiofiles.open(CONFIG_FILE) as f: async with aiofiles.open(CONFIG_FILE) as f:
contents = await f.read() contents = await f.read()
config = json.loads(contents) config = json.loads(contents)
client = AsyncClient(config["homeserver"]) client = AsyncClient(config["homeserver"])
client.access_token = config["access_token"] client.access_token = config["access_token"]
client.user_id = config["user_id"] client.user_id = config["user_id"]
client.device_id = config["device_id"] client.device_id = config["device_id"]
room_id = config["room_id"]
# Now we can send messages as the user # Now we can send messages as the user
await client.room_send( await client.room_send(
room_id, room_id,
message_type="m.room.message", message_type="m.room.message",
content={"msgtype": "m.text", "body": "Hello world!"}, content={"msgtype": "m.text", "body": "Hello world!"},
) )
print("Logged in using stored credentials. Sent a test message.") print("Logged in using stored credentials. Sent a test message.")
# Either way we're logged in here, too # Either way we're logged in here, too
await client.close() await client.close()
asyncio.run(main()) asyncio.run(main())

110
getbot/login.py Executable file
View File

@ -0,0 +1,110 @@
import asyncio
import getpass
import json
import os
import sys
import traceback
import aiofiles
from nio import (
AsyncClient,
AsyncClientConfig,
KeyVerificationCancel,
KeyVerificationEvent,
KeyVerificationKey,
KeyVerificationMac,
KeyVerificationStart,
LocalProtocolError,
LoginResponse,
ToDeviceError,
ToDeviceMessage,
UnknownToDeviceEvent,
)
CONFIG_FILE = "credentials.json"
STORE_PATH = "./store/" # local directory
async def login() -> AsyncClient:
"""Handle login with or without stored credentials."""
# Configuration options for the AsyncClient
client_config = AsyncClientConfig(
max_limit_exceeded=0,
max_timeouts=0,
store_sync_tokens=True,
encryption_enabled=True,
)
# If there are no previously-saved credentials, we'll use the password
if not os.path.exists(CONFIG_FILE):
# print(
# "First time use. Did not find credential file. Asking for "
# "homeserver, user, and password to create credential file."
# )
# homeserver = "https://matrix.example.org"
# homeserver = input(f"Enter your homeserver URL: [{homeserver}] ")
# if not (homeserver.startswith("https://") or homeserver.startswith("http://")):
# homeserver = "https://" + homeserver
# user_id = "@user:example.org"
# user_id = input(f"Enter your full user ID: [{user_id}] ")
# device_name = "matrix-nio"
# device_name = input(f"Choose a name for this device: [{device_name}] ")
homeserver = "https://matrix.domnomnom.com"
user_id = "@getbot:matrix.domnomnom.com"
room_id = "!sZpfYzLsRbnIOKJlPH:matrix.domnomnom.com"
device_name = "whitebox-nio"
if not os.path.exists(STORE_PATH):
os.makedirs(STORE_PATH)
# Initialize the matrix client
client = AsyncClient(
homeserver,
user_id,
store_path=STORE_PATH,
config=client_config,
)
pw = getpass.getpass()
resp = await client.login(password=pw, device_name=device_name)
# check that we logged in successfully
if isinstance(resp, LoginResponse):
write_details_to_disk(resp, homeserver)
else:
print(f'homeserver = "{homeserver}"; user = "{user_id}"')
print(f"Failed to log in: {resp}")
sys.exit(1)
print(
"Logged in using a password. Credentials were stored. "
"On next execution the stored login credentials will be used."
)
# Otherwise the config file exists, so we'll use the stored credentials
else:
# open the file in read-only mode
async with aiofiles.open(CONFIG_FILE, "r") as f:
contents = await f.read()
config = json.loads(contents)
# Initialize the matrix client based on credentials from file
client = AsyncClient(
config["homeserver"],
config["user_id"],
device_id=config["device_id"],
store_path=STORE_PATH,
config=client_config,
)
client.restore_login(
user_id=config["user_id"],
device_id=config["device_id"],
access_token=config["access_token"],
)
print(f"Logged in using stored credentials as {client.user_id}. device_id={client.device_id}")
return client

View File

@ -1,50 +1,56 @@
import asyncio import asyncio
import aiofiles.os
from nio import AsyncClient, MatrixRoom, RoomMessageText from nio import AsyncClient, MatrixRoom, RoomMessageText
import asyncio
import getpass
import json
import os
import sys
import aiofiles.os
import magic
from PIL import Image
from nio import AsyncClient, LoginResponse, UploadResponse
from login import login
async def message_callback(room: MatrixRoom, event: RoomMessageText) -> None: async def message_callback(room: MatrixRoom, event: RoomMessageText) -> None:
print( print(
f"Message received in room {room.display_name}\n" f"Message received in room {room.display_name}\n"
f"{room.user_name(event.sender) or event.sender} | {event.body}"
f"{room.user_name(event.sender)} | {event.body}"
) )
async def main() -> None: async def main() -> None:
client = AsyncClient("https://matrix.domnomnom.com", "@getbot:matrix.domnomnom.com") # async with aiofiles.open(CONFIG_FILE) as f:
# contents = await f.read()
# config = json.loads(contents)
# client = AsyncClient(config["homeserver"])
# client.access_token = config["access_token"]
# client.user_id = config["user_id"]
# client.device_id = config["device_id"]
# room_id = "!sZpfYzLsRbnIOKJlPH:matrix.domnomnom.com"
# image = "/home/get/getbot_profile_pic_256.png"
# # await send_image(client, room_id, image)
# if client.should_upload_keys:
# await client.keys_upload()
client = await login()
client.add_event_callback(message_callback, RoomMessageText) client.add_event_callback(message_callback, RoomMessageText)
print(await client.login(password))
# "Logged in as @alice:example.org device id: RANDOMDID"
# If you made a new room and haven't joined as that user, you can use
# await client.join("your-room-id")
await client.room_send(
room_id="!sZpfYzLsRbnIOKJlPH:matrix.domnomnom.com",
message_type="m.room.message",
content={"msgtype": "m.text", "body": "Hello world!"},
)
await client.sync_forever(timeout=30000) # milliseconds await client.sync_forever(timeout=30000) # milliseconds
asyncio.run(main()) if __name__ == '__main__':
asyncio.run(main())

View File

@ -359,21 +359,26 @@ async def login() -> AsyncClient:
# If there are no previously-saved credentials, we'll use the password # If there are no previously-saved credentials, we'll use the password
if not os.path.exists(CONFIG_FILE): if not os.path.exists(CONFIG_FILE):
print( # print(
"First time use. Did not find credential file. Asking for " # "First time use. Did not find credential file. Asking for "
"homeserver, user, and password to create credential file." # "homeserver, user, and password to create credential file."
) # )
homeserver = "https://matrix.example.org" # homeserver = "https://matrix.example.org"
homeserver = input(f"Enter your homeserver URL: [{homeserver}] ") # homeserver = input(f"Enter your homeserver URL: [{homeserver}] ")
if not (homeserver.startswith("https://") or homeserver.startswith("http://")): # if not (homeserver.startswith("https://") or homeserver.startswith("http://")):
homeserver = "https://" + homeserver # homeserver = "https://" + homeserver
user_id = "@user:example.org" # user_id = "@user:example.org"
user_id = input(f"Enter your full user ID: [{user_id}] ") # user_id = input(f"Enter your full user ID: [{user_id}] ")
# device_name = "matrix-nio"
# device_name = input(f"Choose a name for this device: [{device_name}] ")
homeserver = "https://matrix.domnomnom.com"
user_id = "@getbot:matrix.domnomnom.com"
room_id = "!sZpfYzLsRbnIOKJlPH:matrix.domnomnom.com"
device_name = "whitebox-nio"
device_name = "matrix-nio"
device_name = input(f"Choose a name for this device: [{device_name}] ")
if not os.path.exists(STORE_PATH): if not os.path.exists(STORE_PATH):
os.makedirs(STORE_PATH) os.makedirs(STORE_PATH)