Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ By default the `bot.storage` is in-memory.
Any changes are lost when the bot is stopped or reseted.
For persistent storage to disk, check the SQLite or Redis storage in `storage.py`.

To use Redis with password authentication, add `redis_password` to the storage config:

```python
config = {
"storage": {
"redis_host": "redis",
"redis_port": 6379,
"redis_password": "your_password",
},
}
```

### Command

To implement your own commands, you need to inherent `Command` and overwrite following methods:
Expand Down
5 changes: 4 additions & 1 deletion signalbot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ def __init__(self, config: dict): # noqa: ANN204
else:
self._redis_host = config_storage["redis_host"]
self._redis_port = config_storage["redis_port"]
self.storage = RedisStorage(self._redis_host, self._redis_port)
self._redis_password = config_storage.get("redis_password")
self.storage = RedisStorage(
self._redis_host, self._redis_port, self._redis_password
)
self._logger.info("redis storage initilized")
except Exception: # noqa: BLE001
self.storage = SQLiteStorage()
Expand Down
19 changes: 15 additions & 4 deletions signalbot/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@


class MessageType(Enum):
SYNC_MESSAGE = 1 # Message recieved in a linked device
DATA_MESSAGE = 2 # Message recieved in a primary device
SYNC_MESSAGE = 1 # Message received in a linked device
DATA_MESSAGE = 2 # Message received in a primary device
EDIT_MESSAGE = 3 # Message received is an edit of a previous message
DELETE_MESSAGE = 4 # Message received is a remote delete of a previous message
READ_MESSAGE = 5 # User read some messages


@dataclass
Expand All @@ -37,6 +38,7 @@ class Message:
reaction: str | None = None
mentions: list[str] = field(default_factory=list)
quote: Quote | None = None
read_messages: list[dict] | None = None
target_sent_timestamp: int | None = None
remote_delete_timestamp: int | None = None
raw_message: str | None = None
Expand Down Expand Up @@ -67,8 +69,15 @@ def _extract_message_data(
if sync_message == {}:
raise UnknownMessageFormatError

message_type = MessageType.SYNC_MESSAGE
data_message = sync_message["sentMessage"]
if "readMessages" in sync_message:
message_type = MessageType.READ_MESSAGE
data_message = {
"message": "",
"readMessages": sync_message["readMessages"],
}
else:
message_type = MessageType.SYNC_MESSAGE
data_message = sync_message["sentMessage"]

if "editMessage" in data_message:
message_type = MessageType.EDIT_MESSAGE
Expand Down Expand Up @@ -128,6 +137,7 @@ async def parse(cls, signal: SignalAPI, raw_message_str: str) -> Message:
reaction = cls._parse_reaction(data_message)
mentions = cls._parse_mentions(data_message)
quote = cls._parse_quote(data_message)
read_messages = data_message.get("readMessages")

base64_attachments, attachments_local_filenames, link_previews = [], [], []
view_once = False
Expand All @@ -154,6 +164,7 @@ async def parse(cls, signal: SignalAPI, raw_message_str: str) -> Message:
reaction=reaction,
mentions=mentions,
quote=quote,
read_messages=read_messages,
target_sent_timestamp=target_sent_timestamp,
remote_delete_timestamp=remote_delete_timestamp,
raw_message=raw_message_str,
Expand Down
4 changes: 2 additions & 2 deletions signalbot/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ def delete(self, key: str) -> None:


class RedisStorage(Storage):
def __init__(self, host: str, port: int): # noqa: ANN204
self._redis = redis.Redis(host=host, port=port, db=0)
def __init__(self, host: str, port: int, password: str | None = None): # noqa: ANN204
self._redis = redis.Redis(host=host, port=port, db=0, password=password)

def exists(self, key: str) -> bool:
return self._redis.exists(key)
Expand Down
17 changes: 17 additions & 0 deletions tests/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class TestMessage(unittest.IsolatedAsyncioTestCase):
raw_reaction_message = '{"envelope":{"source":"<source>","sourceNumber":"<source>","sourceUuid":"<uuid>","sourceName":"<name>","sourceDevice":1,"timestamp":1632576001632,"syncMessage":{"sentMessage":{"timestamp":1632576001632,"message":null,"expiresInSeconds":0,"viewOnce":false,"reaction":{"emoji":"👍","targetAuthor":"<target>","targetAuthorNumber":"<target>","targetAuthorUuid":"<uuid>","targetSentTimestamp":1632576001632,"isRemove":false},"mentions":[],"attachments":[],"contacts":[],"groupInfo":{"groupId":"<groupid>","type":"DELIVER"},"destination":null,"destinationNumber":null,"destinationUuid":null}}}}' # noqa: E501
raw_user_chat_message = '{"envelope":{"source":"+490123456789","sourceNumber":"+490123456789","sourceUuid":"<uuid>","sourceName":"<name>","sourceDevice":1,"timestamp":1632576001632,"dataMessage":{"timestamp":1632576001632,"message":"Uhrzeit","expiresInSeconds":0,"viewOnce":false}},"account":"+49987654321","subscription":0}' # noqa: E501
raw_attachment_message = '{"envelope":{"source":"+490123456789","sourceNumber":"+490123456789","sourceUuid":"<uuid>","sourceName":"<name>","sourceDevice":1,"timestamp":1632576001632,"dataMessage":{"timestamp":1632576001632,"message":"Uhrzeit","expiresInSeconds":0,"viewOnce":false, "attachments": [{"contentType": "image/png", "filename": "image.png", "id": "1qeCjjWOOo9Gxv8pfdCw.png","size": 12005}]}},"account":"+49987654321","subscription":0}' # noqa: E501
raw_user_read_message = '{"envelope":{"source":"+490123456789","sourceNumber":"+490123456789","sourceUuid":"<uuid>","sourceName":"<name>","sourceDevice":1,"timestamp":1632576001632,"serverReceivedTimestamp":1632576001632,"serverDeliveredTimestamp":1632576001632,"syncMessage":{"readMessages":[{"sender":"+49987654321","senderNumber":"+49987654321","senderUuid":"<uuid>","timestamp":1632576001632}]}},"account":"+49987654321"}' # noqa: E501

expected_source = "+490123456789"
expected_timestamp = 1632576001632
Expand Down Expand Up @@ -116,6 +117,22 @@ async def test_parse_user_chat_message(self):
self.assertEqual(message.timestamp, TestMessage.expected_timestamp) # noqa: PT009
self.assertIsNone(message.group) # noqa: PT009

async def test_message_read(self):
message = await Message.parse(
self.signal_api, TestMessage.raw_user_read_message
)

self.assertEqual(message.type, MessageType.READ_MESSAGE) # noqa: PT009
self.assertEqual(message.text, "") # noqa: PT009
self.assertIsInstance(message.read_messages, list) # noqa: PT009
self.assertEqual(len(message.read_messages), 1) # noqa: PT009

rm = message.read_messages[0]
self.assertEqual(rm.get("sender"), "+49987654321") # noqa: PT009
self.assertEqual(rm.get("senderNumber"), "+49987654321") # noqa: PT009
self.assertEqual(rm.get("timestamp"), TestMessage.expected_timestamp) # noqa: PT009
self.assertIn("senderUuid", rm) # noqa: PT009


if __name__ == "__main__":
unittest.main()