Compare commits
2 Commits
de772841ad
...
c60809b94a
Author | SHA1 | Date |
---|---|---|
Patrick Neff | c60809b94a | |
Patrick Neff | 01e4e9f18b |
|
@ -140,6 +140,9 @@ cython_debug/
|
||||||
# Project specific
|
# Project specific
|
||||||
credentials.json
|
credentials.json
|
||||||
store/*
|
store/*
|
||||||
|
next_batch
|
||||||
|
fifo
|
||||||
|
bot
|
||||||
|
|
||||||
# VSCode
|
# VSCode
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
1
Pipfile
1
Pipfile
|
@ -7,7 +7,6 @@ verify_ssl = true
|
||||||
flake8 = "*"
|
flake8 = "*"
|
||||||
flake8-quotes = "*"
|
flake8-quotes = "*"
|
||||||
flake8-annotations = "*"
|
flake8-annotations = "*"
|
||||||
black = "*"
|
|
||||||
yapf = "*"
|
yapf = "*"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "b572da4f2fb6963d3f61a7a2234f625fd8c09274e429df366367fb4b359510a0"
|
"sha256": "a17f7aa5b096658663f9e578d693b9f69f01711f3776f8ede29ac1ff34175380"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
@ -331,37 +331,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {
|
"develop": {
|
||||||
"appdirs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
|
|
||||||
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
|
|
||||||
],
|
|
||||||
"version": "==1.4.4"
|
|
||||||
},
|
|
||||||
"attrs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
|
|
||||||
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==19.3.0"
|
|
||||||
},
|
|
||||||
"black": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b",
|
|
||||||
"sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==19.10b0"
|
|
||||||
},
|
|
||||||
"click": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
|
||||||
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
|
||||||
"version": "==7.1.2"
|
|
||||||
},
|
|
||||||
"flake8": {
|
"flake8": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
|
"sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
|
||||||
|
@ -392,13 +361,6 @@
|
||||||
],
|
],
|
||||||
"version": "==0.6.1"
|
"version": "==0.6.1"
|
||||||
},
|
},
|
||||||
"pathspec": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
|
|
||||||
"sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
|
|
||||||
],
|
|
||||||
"version": "==0.8.0"
|
|
||||||
},
|
|
||||||
"pycodestyle": {
|
"pycodestyle": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
|
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
|
||||||
|
@ -415,65 +377,6 @@
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
"version": "==2.2.0"
|
"version": "==2.2.0"
|
||||||
},
|
},
|
||||||
"regex": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
|
|
||||||
"sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
|
|
||||||
"sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
|
|
||||||
"sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
|
|
||||||
"sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
|
|
||||||
"sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
|
|
||||||
"sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
|
|
||||||
"sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
|
|
||||||
"sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
|
|
||||||
"sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
|
|
||||||
"sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
|
|
||||||
"sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
|
|
||||||
"sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
|
|
||||||
"sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
|
|
||||||
"sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
|
|
||||||
"sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
|
|
||||||
"sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
|
|
||||||
"sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
|
|
||||||
"sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
|
|
||||||
"sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
|
|
||||||
"sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
|
|
||||||
],
|
|
||||||
"version": "==2020.7.14"
|
|
||||||
},
|
|
||||||
"toml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
|
|
||||||
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
|
|
||||||
],
|
|
||||||
"version": "==0.10.1"
|
|
||||||
},
|
|
||||||
"typed-ast": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
|
|
||||||
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
|
|
||||||
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
|
|
||||||
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
|
|
||||||
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
|
|
||||||
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
|
|
||||||
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
|
|
||||||
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
|
|
||||||
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
|
|
||||||
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
|
|
||||||
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
|
|
||||||
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
|
|
||||||
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
|
|
||||||
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
|
|
||||||
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
|
|
||||||
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
|
|
||||||
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
|
|
||||||
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
|
|
||||||
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
|
|
||||||
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
|
|
||||||
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
|
|
||||||
],
|
|
||||||
"version": "==1.4.1"
|
|
||||||
},
|
|
||||||
"yapf": {
|
"yapf": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427",
|
"sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427",
|
||||||
|
|
14
README.md
14
README.md
|
@ -6,8 +6,8 @@ Requires libolm to be installed. in Debian this is done with `apt install libolm
|
||||||
|
|
||||||
## Requiements
|
## Requiements
|
||||||
|
|
||||||
- Python 3.8
|
- Python 3.8
|
||||||
- libolm
|
- libolm
|
||||||
|
|
||||||
## Intallation
|
## Intallation
|
||||||
|
|
||||||
|
@ -32,3 +32,13 @@ After that you can begin sending messages
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
matrix-bot message "Message Content" # To send to the default room
|
matrix-bot message "Message Content" # To send to the default room
|
||||||
matrix-bot message -r '!yourRoomId' "Message Content" # To send to a specific room
|
matrix-bot message -r '!yourRoomId' "Message Content" # To send to a specific room
|
||||||
|
|
||||||
|
Or start the bot in daemon mode. In this mode the configured room will be monitored for incoming messages and you can send messages from external scripts with `matrix-botctl`
|
||||||
|
|
||||||
|
cd MATRIX_BOT_DIR
|
||||||
|
source venv/bin/activate
|
||||||
|
matrix-bot
|
||||||
|
|
||||||
|
# Then you can send messages with
|
||||||
|
|
||||||
|
matrix-botctl message "Message Content"
|
||||||
|
|
|
@ -1,33 +1,18 @@
|
||||||
from nio import (
|
from nio import (AsyncClient, AsyncClientConfig, LoginResponse,
|
||||||
AsyncClient,
|
KeyVerificationEvent, KeyVerificationStart,
|
||||||
AsyncClientConfig,
|
KeyVerificationCancel, KeyVerificationKey, KeyVerificationMac,
|
||||||
LoginResponse,
|
ToDeviceError, LocalProtocolError, SyncResponse,
|
||||||
KeyVerificationEvent,
|
RoomMessageText)
|
||||||
KeyVerificationStart,
|
|
||||||
KeyVerificationCancel,
|
|
||||||
KeyVerificationKey,
|
|
||||||
KeyVerificationMac,
|
|
||||||
ToDeviceError,
|
|
||||||
LocalProtocolError,
|
|
||||||
)
|
|
||||||
import click
|
import click
|
||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import signal
|
import markdown
|
||||||
|
|
||||||
# The directory containing the store and credentials file
|
from .config import (CONFIG_FILE, STORE_PATH, NEXT_BATCH_PATH, HOMESERVER_URL,
|
||||||
CONFIG_DIRECTORY = '.'
|
FIFO_PATH)
|
||||||
# file to store credentials in case you want to run program multiple times
|
|
||||||
# login credentials JSON file
|
|
||||||
CONFIG_FILE = os.path.join(CONFIG_DIRECTORY, 'credentials.json')
|
|
||||||
# directory to store persistent data for end-to-end encryption
|
|
||||||
STORE_PATH = os.path.join(CONFIG_DIRECTORY, 'store') # local directory
|
|
||||||
|
|
||||||
# Default Homeserver URL
|
|
||||||
HOMESERVER_URL = 'https://matrix.gaja-group.com'
|
|
||||||
|
|
||||||
|
|
||||||
def write_details_to_disk(resp: LoginResponse, credentials: dict) -> None:
|
def write_details_to_disk(resp: LoginResponse, credentials: dict) -> None:
|
||||||
|
@ -61,16 +46,12 @@ def write_details_to_disk(resp: LoginResponse, credentials: dict) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Bot:
|
class Bot():
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._loop = asyncio.get_event_loop()
|
self._loop = asyncio.get_event_loop()
|
||||||
signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
|
|
||||||
for sig in signals:
|
|
||||||
self._loop.add_signal_handler(
|
|
||||||
sig, lambda sig=sig: asyncio.create_task(self.shutdown(sig)))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def login(cls: any) -> object:
|
async def login(cls: any) -> any:
|
||||||
bot = cls()
|
bot = cls()
|
||||||
await bot._login()
|
await bot._login()
|
||||||
return bot
|
return bot
|
||||||
|
@ -91,24 +72,41 @@ class Bot:
|
||||||
def config(self) -> dict:
|
def config(self) -> dict:
|
||||||
return self._config
|
return self._config
|
||||||
|
|
||||||
def send_text_message_to_room(self, room_id: str, message: str) -> None:
|
async def send_message_to_room(
|
||||||
async def send_message(room_id: str, message: str) -> None:
|
self,
|
||||||
"""Login and wait for and perform emoji verify."""
|
room_id: str,
|
||||||
# Set up event callbacks
|
content: dict,
|
||||||
content = {'msgtype': 'm.text', 'body': message}
|
message_type: str = 'm.room.message') -> None:
|
||||||
await self._login()
|
"""Login and wait for and perform emoji verify."""
|
||||||
if room_id is None:
|
# Set up event callbacks
|
||||||
room_id = self.config['room_id']
|
await self._login()
|
||||||
await self.client.sync(timeout=30000, full_state=True)
|
if room_id is None:
|
||||||
await self.client.room_send(
|
room_id = self.config['room_id']
|
||||||
room_id=room_id,
|
await self.client.sync(timeout=30000, full_state=True)
|
||||||
message_type='m.room.message',
|
await self.client.room_send(
|
||||||
content=content,
|
room_id=room_id,
|
||||||
ignore_unverified_devices=True,
|
message_type=message_type,
|
||||||
)
|
content=content,
|
||||||
await self.client.close()
|
ignore_unverified_devices=True,
|
||||||
|
)
|
||||||
|
await self.client.set_presence('offline')
|
||||||
|
await self.client.close()
|
||||||
|
|
||||||
self.loop.run_until_complete(send_message(room_id, message))
|
async def send_text_to_room(self, room_id: str, message: str) -> None:
|
||||||
|
content = {
|
||||||
|
'msgtype': 'm.text',
|
||||||
|
'format': 'org.matrix.custom.html',
|
||||||
|
'formatted_body': markdown.markdown(message),
|
||||||
|
}
|
||||||
|
await self.send_message_to_room(room_id, content)
|
||||||
|
|
||||||
|
async def send_notice_to_room(self, room_id: str, message: str) -> None:
|
||||||
|
content = {
|
||||||
|
'msgtype': 'm.notice',
|
||||||
|
'format': 'org.matrix.custom.html',
|
||||||
|
'formatted_body': markdown.markdown(message),
|
||||||
|
}
|
||||||
|
await self.send_message_to_room(room_id, content)
|
||||||
|
|
||||||
async def _login(self) -> AsyncClient:
|
async def _login(self) -> AsyncClient:
|
||||||
"""Login to the matrix homeserver defined in the config file.
|
"""Login to the matrix homeserver defined in the config file.
|
||||||
|
@ -131,7 +129,7 @@ class Bot:
|
||||||
if not os.path.exists(STORE_PATH):
|
if not os.path.exists(STORE_PATH):
|
||||||
os.makedirs(STORE_PATH)
|
os.makedirs(STORE_PATH)
|
||||||
|
|
||||||
credentials = self.ask_credentials()
|
credentials = self._ask_credentials()
|
||||||
|
|
||||||
# Initialize the matrix client
|
# Initialize the matrix client
|
||||||
client = AsyncClient(
|
client = AsyncClient(
|
||||||
|
@ -165,7 +163,7 @@ class Bot:
|
||||||
'user_id': credentials['user_id'],
|
'user_id': credentials['user_id'],
|
||||||
'homeserver': credentials['homeserver'],
|
'homeserver': credentials['homeserver'],
|
||||||
'room_id': credentials['room_id'],
|
'room_id': credentials['room_id'],
|
||||||
'device_name': resp['device_name'],
|
'device_name': credentials['device_name'],
|
||||||
'device_id': resp.device_id,
|
'device_id': resp.device_id,
|
||||||
'access_token': resp.access_token,
|
'access_token': resp.access_token,
|
||||||
}
|
}
|
||||||
|
@ -199,6 +197,7 @@ class Bot:
|
||||||
# click.secho('Logged in using stored credentials.', fg='green')
|
# click.secho('Logged in using stored credentials.', fg='green')
|
||||||
|
|
||||||
self._client = client
|
self._client = client
|
||||||
|
await self.client.set_presence('online')
|
||||||
|
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
@ -214,57 +213,100 @@ class Bot:
|
||||||
await asyncio.gather(*tasks, return_exceptions=False)
|
await asyncio.gather(*tasks, return_exceptions=False)
|
||||||
self.loop.stop()
|
self.loop.stop()
|
||||||
|
|
||||||
def verify(self) -> None:
|
async def _verify(self) -> None:
|
||||||
async def verify() -> None:
|
"""Login and wait for and perform emoji verify."""
|
||||||
"""Login and wait for and perform emoji verify."""
|
# Set up event callbacks
|
||||||
# Set up event callbacks
|
client = await self._login()
|
||||||
client = await self._login()
|
client.add_to_device_callback(self._to_device_callback,
|
||||||
client.add_to_device_callback(self.to_device_callback,
|
(KeyVerificationEvent, ))
|
||||||
(KeyVerificationEvent, ))
|
# Sync encryption keys with the server
|
||||||
# Sync encryption keys with the server
|
# Required for participating in encrypted rooms
|
||||||
# Required for participating in encrypted rooms
|
if self.client.should_upload_keys:
|
||||||
if self.client.should_upload_keys:
|
await self.client.keys_upload()
|
||||||
await self.client.keys_upload()
|
click.secho('\nStarting verification process...', bold=True)
|
||||||
click.secho('\nStarting verification process...', bold=True)
|
click.secho(
|
||||||
click.secho(
|
'\nThis program is ready and waiting for the other '
|
||||||
'\nThis program is ready and waiting for the other '
|
'party to initiate an emoji verification with us by '
|
||||||
'party to initiate an emoji verification with us by '
|
'selecting "Verify by Emoji" in their Matrix '
|
||||||
'selecting "Verify by Emoji" in their Matrix '
|
'client.',
|
||||||
'client.',
|
fg='green',
|
||||||
fg='green',
|
)
|
||||||
)
|
await self.client.sync_forever(timeout=30000, full_state=True)
|
||||||
await self.client.sync_forever(timeout=30000, full_state=True)
|
|
||||||
|
|
||||||
self.loop.run_until_complete(verify())
|
def verify(self) -> None:
|
||||||
|
self._run_async(self._verify())
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
self._run_async(self._run())
|
||||||
|
|
||||||
|
async def _run(self) -> None:
|
||||||
|
fifo_name = FIFO_PATH
|
||||||
|
|
||||||
|
if os.path.exists(fifo_name):
|
||||||
|
os.remove(fifo_name)
|
||||||
|
|
||||||
|
os.mkfifo(fifo_name)
|
||||||
|
pipe_fd = os.open(fifo_name, (os.O_RDONLY | os.O_NONBLOCK))
|
||||||
|
|
||||||
|
client = await self._login()
|
||||||
|
|
||||||
|
# we read the previously-written token...
|
||||||
|
next_batch_name = NEXT_BATCH_PATH
|
||||||
|
if os.path.exists(next_batch_name):
|
||||||
|
with open(next_batch_name, 'r') as next_batch_token:
|
||||||
|
# ... and well async_client to use it
|
||||||
|
self.client.next_batch = next_batch_token.read()
|
||||||
|
|
||||||
|
client.add_response_callback(self._sync_callback, SyncResponse)
|
||||||
|
client.add_event_callback(self._room_text_message_callback,
|
||||||
|
RoomMessageText)
|
||||||
|
await asyncio.gather(
|
||||||
|
self.client.sync_forever(timeout=30000, full_state=True),
|
||||||
|
self._fifo_reader(pipe_fd),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_async(self, future: asyncio.Future) -> None:
|
||||||
|
try:
|
||||||
|
self.loop.run_until_complete(future)
|
||||||
|
except Exception:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
sys.exit(1)
|
||||||
|
except asyncio.exceptions.CancelledError:
|
||||||
|
sys.exit(0)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
def _ask_credentials(self) -> dict:
|
def _ask_credentials(self) -> dict:
|
||||||
"""Ask the user for credentials
|
"""Ask the user for credentials
|
||||||
"""
|
"""
|
||||||
homeserver = HOMESERVER_URL
|
try:
|
||||||
homeserver = click.prompt(
|
homeserver = HOMESERVER_URL
|
||||||
click.style('Enter your homeserver URL', bold=True),
|
homeserver = click.prompt(
|
||||||
default=homeserver,
|
click.style('Enter your homeserver URL', bold=True),
|
||||||
)
|
default=homeserver,
|
||||||
|
)
|
||||||
|
|
||||||
if not homeserver.startswith('https://'):
|
if not homeserver.startswith('https://'):
|
||||||
homeserver = 'https://' + homeserver
|
homeserver = 'https://' + homeserver
|
||||||
|
|
||||||
user_id = '@user:gaja-group.com'
|
user_id = '@user:gaja-group.com'
|
||||||
user_id = click.prompt(click.style('Enter your full user ID',
|
user_id = click.prompt(click.style('Enter your full user ID',
|
||||||
bold=True),
|
bold=True),
|
||||||
default=user_id)
|
default=user_id)
|
||||||
|
|
||||||
device_name = 'matrix-bot'
|
device_name = 'matrix-bot'
|
||||||
device_name = click.prompt(
|
device_name = click.prompt(
|
||||||
click.style('Choose a name for this device', bold=True),
|
click.style('Choose a name for this device', bold=True),
|
||||||
default=device_name,
|
default=device_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
room_id = '!yourRoomId:gaja-group.com'
|
room_id = '!yourRoomId:gaja-group.com'
|
||||||
room_id = click.prompt(
|
room_id = click.prompt(
|
||||||
click.style('Enter a default room ID to send to', bold=True),
|
click.style('Enter a default room ID to send to', bold=True),
|
||||||
default=room_id,
|
default=room_id,
|
||||||
)
|
)
|
||||||
|
except click.exceptions.Abort:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'homeserver': homeserver,
|
'homeserver': homeserver,
|
||||||
|
@ -273,7 +315,7 @@ class Bot:
|
||||||
'room_id': room_id,
|
'room_id': room_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def to_device_callback(self, event): # noqa
|
async def _to_device_callback(self, event): # noqa
|
||||||
"""Handle events sent to device."""
|
"""Handle events sent to device."""
|
||||||
try:
|
try:
|
||||||
client = self.client
|
client = self.client
|
||||||
|
@ -455,21 +497,60 @@ class Bot:
|
||||||
# f'sas.timed_out = {sas.timed_out}\n'
|
# f'sas.timed_out = {sas.timed_out}\n'
|
||||||
# f'sas.verified = {sas.verified}\n'
|
# f'sas.verified = {sas.verified}\n'
|
||||||
# f'sas.verified_devices = {sas.verified_devices}\n')
|
# f'sas.verified_devices = {sas.verified_devices}\n')
|
||||||
click.secho('Emoji verification was successful!',
|
click.secho(
|
||||||
fg='green')
|
'Emoji verification was successful! Please use Ctrl+C '
|
||||||
await self.shutdown([], self._loop)
|
'to exit.',
|
||||||
|
fg='green')
|
||||||
else:
|
else:
|
||||||
print(f'Received unexpected event type {type(event)}. '
|
print(f'Received unexpected event type {type(event)}. '
|
||||||
f'Event is {event}. Event will be ignored.')
|
f'Event is {event}. Event will be ignored.')
|
||||||
except BaseException:
|
except BaseException:
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
def _sync_callback(self, event: any) -> None:
|
||||||
|
with open(NEXT_BATCH_PATH, 'w') as next_batch_token:
|
||||||
|
next_batch_token.write(event.next_batch)
|
||||||
|
|
||||||
@click.group()
|
async def _room_text_message_callback(self, room: any,
|
||||||
|
message: any) -> None:
|
||||||
|
if room.room_id == self.config['room_id']:
|
||||||
|
if message.body.startswith('!help'):
|
||||||
|
await self.send_notice_to_room(
|
||||||
|
room.room_id, """##### Usage
|
||||||
|
|
||||||
|
**!help** - displays this help message
|
||||||
|
""")
|
||||||
|
|
||||||
|
async def _fifo_reader(self, pipe_fd: str) -> None:
|
||||||
|
with os.fdopen(pipe_fd) as file:
|
||||||
|
while True:
|
||||||
|
data = file.read()
|
||||||
|
if len(data) > 0:
|
||||||
|
try:
|
||||||
|
data = data.split('\0')
|
||||||
|
for d in data:
|
||||||
|
if (len(d) > 0):
|
||||||
|
cmd = json.loads(d)
|
||||||
|
room_id = self.config['room_id']
|
||||||
|
if cmd['type'] == 'message':
|
||||||
|
if 'room_id' in cmd.keys():
|
||||||
|
room_id = cmd['room_id']
|
||||||
|
await self.send_notice_to_room(
|
||||||
|
room_id, cmd['content'])
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print('JSON decode error:', e)
|
||||||
|
pass
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
@click.group(invoke_without_command=True)
|
||||||
@click.option('-v', '--verbose', count=True)
|
@click.option('-v', '--verbose', count=True)
|
||||||
def cli(verbose: int) -> None:
|
@click.pass_context
|
||||||
|
def cli(ctx: click.Context, verbose: int) -> None:
|
||||||
# click.secho('Matrix Bot\n', bold='true')
|
# click.secho('Matrix Bot\n', bold='true')
|
||||||
pass
|
if ctx.invoked_subcommand is None:
|
||||||
|
bot = Bot()
|
||||||
|
bot.run()
|
||||||
|
|
||||||
|
|
||||||
@cli.command('verify')
|
@cli.command('verify')
|
||||||
|
@ -483,7 +564,13 @@ def verify_command() -> None:
|
||||||
@click.option('-r', '--room', help='the room to send to')
|
@click.option('-r', '--room', help='the room to send to')
|
||||||
def send_command(message: str, room: str) -> None:
|
def send_command(message: str, room: str) -> None:
|
||||||
bot = Bot()
|
bot = Bot()
|
||||||
bot.send_text_message_to_room(room, message)
|
bot._run_async(bot.send_text_to_room(room, message))
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command('run')
|
||||||
|
def run() -> None:
|
||||||
|
bot = Bot()
|
||||||
|
bot.run()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import click
|
||||||
|
import json
|
||||||
|
|
||||||
|
from .config import FIFO_PATH
|
||||||
|
|
||||||
|
|
||||||
|
def send_message(message: str) -> None:
|
||||||
|
fifo_name = FIFO_PATH
|
||||||
|
|
||||||
|
if not os.path.exists(fifo_name):
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Starting two readers and one writer, but only one reader
|
||||||
|
# will be reading at the same time.
|
||||||
|
content = {'type': 'message', 'content': message}
|
||||||
|
with open(fifo_name, 'w') as file:
|
||||||
|
file.write(json.dumps(content))
|
||||||
|
file.write('\0')
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli() -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command()
|
||||||
|
@click.argument('message', nargs=-1)
|
||||||
|
def message(message: list) -> None:
|
||||||
|
send_message(' '.join(message))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
cli()
|
|
@ -0,0 +1,15 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
# The directory containing the store and credentials file
|
||||||
|
CONFIG_DIRECTORY = '.'
|
||||||
|
# file to store credentials in case you want to run program multiple times
|
||||||
|
# login credentials JSON file
|
||||||
|
CONFIG_FILE = os.path.join(CONFIG_DIRECTORY, 'credentials.json')
|
||||||
|
# directory to store persistent data for end-to-end encryption
|
||||||
|
STORE_PATH = os.path.join(CONFIG_DIRECTORY, 'store') # local directory
|
||||||
|
FIFO_PATH = os.path.join(CONFIG_DIRECTORY, 'fifo') # local directory
|
||||||
|
NEXT_BATCH_PATH = os.path.join(CONFIG_DIRECTORY,
|
||||||
|
'next_batch') # local directory
|
||||||
|
|
||||||
|
# Default Homeserver URL
|
||||||
|
HOMESERVER_URL = 'https://matrix.gaja-group.com'
|
7
setup.py
7
setup.py
|
@ -4,13 +4,10 @@ setup(
|
||||||
name='matrix-bot',
|
name='matrix-bot',
|
||||||
version='0.1',
|
version='0.1',
|
||||||
py_modules=['matrix_bot'],
|
py_modules=['matrix_bot'],
|
||||||
install_requires=[
|
install_requires=['Click', 'matrix-nio[e2e]', 'markdown'],
|
||||||
'Click',
|
|
||||||
'matrix-nio[e2e]',
|
|
||||||
'markdown'
|
|
||||||
],
|
|
||||||
entry_points="""
|
entry_points="""
|
||||||
[console_scripts]
|
[console_scripts]
|
||||||
matrix-bot=matrix_bot.bot:cli
|
matrix-bot=matrix_bot.bot:cli
|
||||||
|
matrix-botctl=matrix_bot.botctl:cli
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue