Skip to content

Commit

Permalink
new feature: --room-dm-create-allow-duplicates
Browse files Browse the repository at this point in the history
By default this is off (not used) to avoid creating multiple DM rooms.
But multiple DM rooms can be created by setting this flag or if an old DM room invitation has not been yet accepted.

See also Issue #140
  • Loading branch information
8go committed Oct 15, 2023
1 parent 586a047 commit fe676b9
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 18 deletions.
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ usage: matrix-commander [--usage] [-h] [--manual] [--readme] [-d]
[--room-default DEFAULT_ROOM]
[--room-create ROOM_ALIAS [ROOM_ALIAS ...]]
[--room-dm-create USER [USER ...]]
[--room-dm-create-allow-duplicates]
[--room-join ROOM [ROOM ...]]
[--room-leave ROOM [ROOM ...]]
[--room-forget ROOM [ROOM ...]]
Expand Down Expand Up @@ -1036,9 +1037,27 @@ options:
--topic, --alias to add names, topics and aliases to
the room(s) to be created. DM rooms are by default
created encrypted; to overwrite that and to create a
room with encryption disabled use '--plain'. Room id,
room alias, encryption and other fields are printed as
output, one line per created room.
room with encryption disabled use '--plain'. See
option '--room-dm-create-allow-duplicates'. If not
used, then an invitation-accepted DM room is searched.
If an existing DM room is found, no new DM room will
be created. If currently no invitation-accepted DM
room exists or --room-dm-create-allow-duplicates is
used, then a new DM will be created. Note, that one
can create/have any number of DM rooms with the same
person. Room id, room alias, encryption and other
fields are printed as output, one line per created
room. If a room is not created because one already
exists, then the room id of the first DM room found is
printed, but neither the alias nor other fields.
--room-dm-create-allow-duplicates
Allow creating duplicate DM rooms. Details:: By
default, if this option is bot used duplicates are
avoided. Actions that support this option are: --room-
dm-create. To overwrite that default and to allow the
creation of a DM room even if a DM room already
exists, use '--room-dm-create-allow-duplicates'. See
the --room-dm-create commands.
--room-join ROOM [ROOM ...]
Join one room or multiple rooms. Details:: One or
multiple room aliases can be specified. The room (or
Expand Down Expand Up @@ -1901,7 +1920,7 @@ options:
the program will continue to run. This is useful for
having version number in the log files.
You are running version 7.4.0 2023-10-12. Enjoy, star on Github and contribute
You are running version 7.5.0 2023-10-15. Enjoy, star on Github and contribute
by submitting a Pull Request. Also have a look at matrix-commander-tui.
```

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7.4.0
7.5.0
Binary file removed dist/matrix-commander-7.4.0.tar.gz
Binary file not shown.
Binary file added dist/matrix-commander-7.5.0.tar.gz
Binary file not shown.
Binary file removed dist/matrix_commander-7.4.0-py3-none-any.whl
Binary file not shown.
Binary file added dist/matrix_commander-7.5.0-py3-none-any.whl
Binary file not shown.
4 changes: 3 additions & 1 deletion help.help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ matrix-commander supports these arguments:
Create one or multiple rooms for given alias(es).
--room-dm-create USER [USER ...]
Create one or multiple DM rooms with the specified users.
--room-dm-create-allow-duplicates
Allow creating duplicate DM rooms.
--room-join ROOM [ROOM ...]
Join one room or multiple rooms.
--room-leave ROOM [ROOM ...]
Expand Down Expand Up @@ -202,5 +204,5 @@ delete-content ROOM_ID EVENT_ID REASON [ROOM_ID EVENT_ID REASON ...]
-v [PRINT|CHECK], -V [PRINT|CHECK], --version [PRINT|CHECK]
Print version information or check for updates.

You are running version 7.4.0 2023-10-12. Enjoy, star on Github and contribute
You are running version 7.5.0 2023-10-15. Enjoy, star on Github and contribute
by submitting a Pull Request. Also have a look at matrix-commander-tui.
27 changes: 23 additions & 4 deletions help.manual.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ usage: matrix-commander [--usage] [-h] [--manual] [--readme] [-d]
[--room-default DEFAULT_ROOM]
[--room-create ROOM_ALIAS [ROOM_ALIAS ...]]
[--room-dm-create USER [USER ...]]
[--room-dm-create-allow-duplicates]
[--room-join ROOM [ROOM ...]]
[--room-leave ROOM [ROOM ...]]
[--room-forget ROOM [ROOM ...]]
Expand Down Expand Up @@ -280,9 +281,27 @@ options:
--topic, --alias to add names, topics and aliases to
the room(s) to be created. DM rooms are by default
created encrypted; to overwrite that and to create a
room with encryption disabled use '--plain'. Room id,
room alias, encryption and other fields are printed as
output, one line per created room.
room with encryption disabled use '--plain'. See
option '--room-dm-create-allow-duplicates'. If not
used, then an invitation-accepted DM room is searched.
If an existing DM room is found, no new DM room will
be created. If currently no invitation-accepted DM
room exists or --room-dm-create-allow-duplicates is
used, then a new DM will be created. Note, that one
can create/have any number of DM rooms with the same
person. Room id, room alias, encryption and other
fields are printed as output, one line per created
room. If a room is not created because one already
exists, then the room id of the first DM room found is
printed, but neither the alias nor other fields.
--room-dm-create-allow-duplicates
Allow creating duplicate DM rooms. Details:: By
default, if this option is bot used duplicates are
avoided. Actions that support this option are: --room-
dm-create. To overwrite that default and to allow the
creation of a DM room even if a DM room already
exists, use '--room-dm-create-allow-duplicates'. See
the --room-dm-create commands.
--room-join ROOM [ROOM ...]
Join one room or multiple rooms. Details:: One or
multiple room aliases can be specified. The room (or
Expand Down Expand Up @@ -1145,5 +1164,5 @@ options:
the program will continue to run. This is useful for
having version number in the log files.

You are running version 7.4.0 2023-10-12. Enjoy, star on Github and contribute
You are running version 7.5.0 2023-10-15. Enjoy, star on Github and contribute
by submitting a Pull Request. Also have a look at matrix-commander-tui.
3 changes: 2 additions & 1 deletion help.usage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ usage: matrix-commander [--usage] [-h] [--manual] [--readme] [-d]
[--room-default DEFAULT_ROOM]
[--room-create ROOM_ALIAS [ROOM_ALIAS ...]]
[--room-dm-create USER [USER ...]]
[--room-dm-create-allow-duplicates]
[--room-join ROOM [ROOM ...]]
[--room-leave ROOM [ROOM ...]]
[--room-forget ROOM [ROOM ...]]
Expand Down Expand Up @@ -64,5 +65,5 @@ usage: matrix-commander [--usage] [-h] [--manual] [--readme] [-d]
[--room-invites [LIST|JOIN|LIST+JOIN]]
[-v [PRINT|CHECK]]

You are running version 7.4.0 2023-10-12. Enjoy, star on Github and contribute
You are running version 7.5.0 2023-10-15. Enjoy, star on Github and contribute
by submitting a Pull Request. Also have a look at matrix-commander-tui.
193 changes: 187 additions & 6 deletions matrix_commander/matrix_commander.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@
HAVE_OPENID = False

# version number
VERSION = "2023-10-12"
VERSIONNR = "7.4.0"
VERSION = "2023-10-15"
VERSIONNR = "7.5.0"
# matrix-commander; for backwards compitability replace _ with -
PROG_WITHOUT_EXT = os.path.splitext(os.path.basename(__file__))[0].replace(
"_", "-"
Expand Down Expand Up @@ -213,7 +213,7 @@
# increment this number and use new incremented number for next warning
# last unique Wxxx warning number used: W112:
# increment this number and use new incremented number for next error
# last unique Exxx error number used: E249:
# last unique Exxx error number used: E251


class LooseVersion:
Expand Down Expand Up @@ -1612,7 +1612,7 @@ async def determine_dm_rooms(
"""
rooms = []
if not users:
gs.log.debug(f"Room(s) from --user: {rooms}, no users were specified.")
gs.log.debug(f"Room(s) from --user: {users}, no users were specified.")
return rooms
sender = credentials["user_id"] # who am i
gs.log.debug(f"Trying to get members for all rooms of sender: {sender}")
Expand Down Expand Up @@ -1692,7 +1692,127 @@ async def determine_dm_rooms(
)
gs.err_count += 1
rooms = list(dict.fromkeys(rooms)) # remove duplicates in list
gs.log.debug(f"Room(s) from --user: {rooms}")
gs.log.debug(
f"Found these DM room(s) for these users: "
f"users: {users}, rooms: {rooms}"
)
return rooms


async def determine_dm_rooms_for_user(
user: str, client: AsyncClient, credentials: dict
) -> list:
"""Determine the DM rooms for one user.
These rooms we label DM (direct messaging) rooms.
By that we means rooms that only have 2 members, and these two
members being the sender and the recipient in question.
We do not care about 'is_group' or 'is_direct' flags (hints).
If given a user and known the sender, we try to find a matching room.
There might be 0, 1, or more matching rooms. If 0, then giver error
and the user should run --room-invite first. if 1 found, use it.
If more than 1 found, just use 1 of them arbitrarily.
The steps are:
- get all rooms where sender is member
- get all members to these rooms
- check if there is a room with just 2 members and them
being sender and recipient (user from users arg)
In order to match a user to a RoomMember we allow 3 choices:
- user_id: perfect match, is unique, full user id, e.g. "@user:example.org"
- user_id without homeserver domain: partial user id, e.g. "@user"
this partial user will be completed by adding the homeserver of the
sender to the end, i.e. assuming that sender and receiver are on the
same homeserver.
- display name: be careful, display names are NOT unique, you could be
mistaken and by error send to the wrong person.
'--joined-members "*"' shows you the display names in the middle column
Arguments:
---------
users: list(str): list of user_ids
try to find a matching DM room for each user
client: AsyncClient: client, allows as to query the server
credentials: dict: allows to get the user_id of sender
Returns a list of found DM rooms. List may be empty if no matches were
found.
"""
rooms = []
if not user:
gs.log.debug(f"Room(s) from user: {user}, no user was specified.")
return rooms
sender = credentials["user_id"] # who am i
gs.log.debug(f"Trying to get members for all rooms of sender: {sender}")
resp = await client.joined_rooms()
if isinstance(resp, JoinedRoomsError):
gs.log.error(
"E249: "
f"joined_rooms failed with {privacy_filter(str(resp))}. "
"Not able to "
"get all rooms. "
f"Not able to find DM rooms for sender {sender}. "
)
gs.err_count += 1
senderrooms = []
else:
gs.log.debug(
f"joined_rooms successful with {privacy_filter(str(resp))}"
)
senderrooms = resp.rooms
for room in senderrooms:
resp = await client.joined_members(room)
if isinstance(resp, JoinedMembersError):
gs.log.error(
"E250: "
f"joined_members failed with {privacy_filter(str(resp))}. "
"Not able to "
f"get room members for room {room}. "
f"Not able to find DM rooms for sender {sender}. "
f"Not able to know if DM room for user {user} exists."
)
gs.err_count += 1
else:
# resp.room_id
# resp.members = List[RoomMember] ; RoomMember
# member.user_id
# member.display_name
# member.avatar_url
gs.log.debug(
f"joined_members successful with {privacy_filter(str(resp))}"
)
if resp.members and len(resp.members) == 2:
if resp.members[0].user_id == sender:
# sndr = resp.members[0]
rcvr = resp.members[1]
elif resp.members[1].user_id == sender:
# sndr = resp.members[1]
rcvr = resp.members[0]
else:
# sndr = None
rcvr = None
gs.log.error(
"E251: "
f"Sender does not match {privacy_filter(str(resp))}"
)
gs.err_count += 1
if (
rcvr
and user == rcvr.user_id
or short_user_name_to_user_id(user, credentials)
== rcvr.user_id
or user == rcvr.display_name
):
rooms.append(resp.room_id)
rooms = list(dict.fromkeys(rooms)) # remove duplicates in list
if not rooms:
gs.log.debug(f"No DM room found for user {user}.")
gs.log.debug(
f"Found these DM room(s) for this user: user: {user}, rooms: {rooms}"
)
return rooms


Expand Down Expand Up @@ -2051,7 +2171,43 @@ async def action_room_dm_create(client: AsyncClient, credentials: dict):
f'room aliases "{room_aliases}", '
f'names "{names}", and topics "{topics}".'
)
gs.log.debug(
"Option --room-dm-create-allow-duplicates has value "
f"{gs.pa.room_dm_create_allow_duplicates}."
)
for user in users:
# see Issue #140
if not gs.pa.room_dm_create_allow_duplicates:
existing_dm_rooms = await determine_dm_rooms_for_user(
user, client, credentials
)
if existing_dm_rooms:
room_id = existing_dm_rooms[0]
gs.log.info(
f'DM room(s) with user "{user}" '
"already exist(s). These DM rooms were found: "
f"{existing_dm_rooms}. "
"Not creating a new room. "
"Ignoring --room-dm-create for this "
f"user {user}."
)
# output format controlled via --output flag
text = f"{room_id}"
# Object of type RoomCreateResponse is not JSON
# serializable, hence we use the dictionary.
json_max = {} # empty dict
# resp has only 1 useful useful member: room_id
json_max.update({"room_id": room_id}) # add dict items
json_ = json_max.copy()
json_spec = None
print_output(
gs.pa.output,
text=text,
json_=json_,
json_max=json_max,
json_spec=json_spec,
)
continue
try:
alias = room_aliases[index]
alias = alias.replace(r"\!", "!") # remove possible escape
Expand Down Expand Up @@ -7277,8 +7433,31 @@ def main_inner(
"DM rooms are by default created encrypted; "
"to overwrite that and to create a room with encryption disabled "
"use '--plain'. "
"See option '--room-dm-create-allow-duplicates'. If not used, "
"then an invitation-accepted DM room is searched. If an existing "
"DM room is found, no new DM room will be created. If currently "
"no invitation-accepted DM room exists or "
"--room-dm-create-allow-duplicates is used, then a new DM will be "
"created. Note, that one can create/have any number of DM rooms "
"with the same person. "
"Room id, room alias, encryption and other fields "
"are printed as output, one line per created room.",
"are printed as output, one line per created room. "
"If a room is not created because one already exists, "
"then the room id of the first DM room found is printed, "
"but neither the alias nor other fields.",
)
ap.add_argument(
"--room-dm-create-allow-duplicates",
required=False,
action="store_true",
help="Allow creating duplicate DM rooms. "
"Details:: By default, if this option is bot used "
"duplicates are avoided. "
"Actions that support this option are: --room-dm-create. "
"To overwrite that default and to allow the creation of a DM room "
"even if a DM room already exists, "
"use '--room-dm-create-allow-duplicates'. "
"See the --room-dm-create commands.",
)
ap.add_argument(
"--room-join",
Expand Down Expand Up @@ -8837,6 +9016,8 @@ def main_inner(
Create one or multiple rooms for given alias(es).
<--room-dm-create> USER [USER ...]
Create one or multiple DM rooms with the specified users.
<--room-dm-create-allow-duplicates>
Allow creating duplicate DM rooms.
<--room-join> ROOM [ROOM ...]
Join one room or multiple rooms.
<--room-leave> ROOM [ROOM ...]
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# https://packaging.python.org/en/latest/tutorials/packaging-projects/
# https://setuptools.pypa.io/en/latest/userguide/
name = matrix-commander
version = 7.4.0
version = 7.5.0
author = 8go
description = A simple command-line Matrix client
long_description = file: PyPi-Instructions.md, README.md
Expand Down

0 comments on commit fe676b9

Please sign in to comment.