-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add script to kick idle bridged users from a room #340
Conversation
python remove-idle-users.py --token=someaccesstoken23478586496849569 --alias='#somealias:some.domain' --homeserver=http://some.domain --domain=some.domain --since=50 --template=@irc_$NICK --token : the access token for the AS --alias : the alias of the room to kick from --homeserver : the URL of the HS --domain : the domain part of the HS URL --template : the userPattern template (in config.sample.yaml, matrixClients)
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser("Remove idle users from a given Matrix room") | ||
parser.add_argument("-t", "--token", help="The AS token", required=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically any token, not just AS.
parser.add_argument("-t", "--token", help="The AS token", required=True) | ||
parser.add_argument("-a", "--alias", help="The alias of the room eg '#freenode_#matrix-dev:matrix.org'", required=True) | ||
parser.add_argument("-u", "--homeserver", help="Base homeserver URL eg 'https://matrix.org'", required=True) | ||
parser.add_argument("-d", "--domain", help=" matrix.org'", required=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'
mismatch. Also missing the sentence!
parser.add_argument("-a", "--alias", help="The alias of the room eg '#freenode_#matrix-dev:matrix.org'", required=True) | ||
parser.add_argument("-u", "--homeserver", help="Base homeserver URL eg 'https://matrix.org'", required=True) | ||
parser.add_argument("-d", "--domain", help=" matrix.org'", required=True) | ||
parser.add_argument("-s", "--since", type=int, help="Since idle users have been offline for", required=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing units and example.
|
||
def main(token, alias, homeserver, homeserver_domain, since, user_template): | ||
print("Removing idle users in %s" % alias) | ||
token = token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhh, nop?
|
||
def get_room_id(homeserver, alias, token): | ||
res = requests.get(homeserver + "/_matrix/client/r0/directory/room/" + urllib.quote(alias) + "?access_token=" + token).json() | ||
return res.get("room_id", None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Rather than return a nullable room_id
, I would just do:
res = requests.get(homeserver + "/_matrix/client/r0/directory/room/" + urllib.quote(alias) + "?access_token=" + token)
res.raise_for_status()
return res.json()["room_id"]
This will throw on non-2xx and if room_id
doesn't exist, both of which you want to do. You can then remove the null guard on :94
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then you would get a quite useless error when the room_id
doesn't get sent back? If this does indicate that the server couldn't resolve it it, would it not be better to throw an error that suggests as such?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah but then you don't get to see the error. Fair point, I'll have it raise for status.
return get_last_active_ago(homeserver, user_id, token) > activity_threshold_ms | ||
|
||
def get_idle_users(homeserver, room_id, token, since): | ||
res = requests.get(homeserver + "/_matrix/client/r0/rooms/" + urllib.quote(room_id) + "/members?access_token=" + token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use this API, it'll take far too long. Instead, use /joined_members
- https://github.com/matrix-org/gomatrix/blob/master/client.go#L546
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should really be documented... would have saved me some time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a new API Erik added not long ago, hence missing docs.
"user_id": user_id | ||
}) | ||
) | ||
res.raise_for_status() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't raise for status here. It'll be annoying as all hell if it spends the time working out who to kick then dies on the first failure. I would add the failures to a list and then whine at the end saying "I couldn't kick these guys because $RESPONSE_JSON".
parser.add_argument("-u", "--homeserver", help="Base homeserver URL eg 'https://matrix.org'", required=True) | ||
parser.add_argument("-d", "--domain", help="Domain of the homserver eg 'matrix.org'", required=True) | ||
parser.add_argument("-s", "--since", type=int, help="Days since idle users have been offline for eg '30'", required=True) | ||
parser.add_argument("-e", "--template", help="User template to determine whether a user should be kicked. E.g. @$SERVER_$NICK", required=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just do prefix matching to save yourself the hassle from trying to work out what to correctly parse things as. It also makes it clearer to the end-user what they should put as a template value ("@freenode_" for example). Bear in mind people who run this may not know the IRC bridge config file syntax so $SERVER
and $NICK
may be foreign to them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh fair point. Matching by static prefix string makes sense.
…cesses, add summary of failures
print("There are %s idle users in %s" % (len(user_ids), room_id)) | ||
for user_id in user_ids: | ||
# Ignore users that do not start with the user_prefix | ||
if not user_id.startswith(user_prefix): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We surely want the opposite here. I want to ignore users that start with @freenode_
because they are managed by the bridge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The continue
on the line after makes sure they are skipped
res = requests.post( | ||
homeserver + "/_matrix/client/r0/rooms/" + urllib.quote(room_id) + "/kick?access_token=" + token, | ||
data = json.dumps({ | ||
"reason": reason, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
}) | ||
) | ||
if res.status_code >= 400: | ||
failure_responses.append({ "user_id": user_id, "response_json": res.json()}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No guarantee that the server returned JSON in this case. I'm guessing requests
will throw if you call .json()
in that case? No idea. We should handle it though (I'm thinking 504 Gateway Timeouts from the Apache).
failure_responses.append({ "user_id": user_id, "response_json": res.json()}) | ||
else: | ||
count += 1 | ||
print("Kicked %s users in total" % count) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More stats in general would be super useful for me:
- How many kicks went through (
count
) - How many kicks were attempted (
count
+len(failure_responses)
) - How many kicks failed (
len(failure_responses)
)
Probably tied together as:
Kicked 44/55 users in total
return | ||
print("Could not kick the following users:") | ||
for failure in failure_responses: | ||
print("%s : %s - %s" % (failure["user_id"], failure["response_json"]["error"], failure["response_json"]["errcode"])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No guarantees that error
and errcode
even exist. It would be a shame to die when logging the failed requests. I would just log the dict
.
parser.add_argument("-u", "--homeserver", help="Base homeserver URL eg 'https://matrix.org'", required=True) | ||
parser.add_argument("-d", "--domain", help="Domain of the homserver eg 'matrix.org'", required=True) | ||
parser.add_argument("-s", "--since", type=int, help="Days since idle users have been offline for eg '30'", required=True) | ||
parser.add_argument("-p", "--prefix", help="User prefix to determine whether a user should be kicked. E.g. @freenode_", required=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically the prefix isn't required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible that the bridge does have enough power to kick users that it does not have claim to, so I suppose it could be optional. Although I'm not really sure why an AS token would be used to kick real users.
parser.add_argument("-t", "--token", help="The access token", required=True) | ||
parser.add_argument("-a", "--alias", help="The alias of the room eg '#freenode_#matrix-dev:matrix.org'", required=True) | ||
parser.add_argument("-u", "--homeserver", help="Base homeserver URL eg 'https://matrix.org'", required=True) | ||
parser.add_argument("-d", "--domain", help="Domain of the homserver eg 'matrix.org'", required=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this A Thing? You never use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, 'twas left over from the user template thing
- Removed homeserver_domain as it is not used - Use membership state API, as Synapse doesn't pass the reason on /kick - Handle non JSON body error responses - Improved logging
failure["response_json"] = res.json() | ||
except Exception as e: | ||
print("Could not get JSON body from failure response: %s" % e) | ||
failure_responses.append() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appending nothing?
else: | ||
count += 1 | ||
print("Kicked %s users in total" % count) | ||
print("Kicked %s/%s users in total (%s failed requests)" % (count, len(user_ids), len(failure_responses))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My earlier comment was explicit for a reason: len(user_ids)
will include ones you want to filter out according to prefix
. It's probably fine since you're giving all the information required to work out the values, but it'll be confusing to see things like:
Kicked 5/10 users in total (2 failed requests)
and wonder what happened to the remaining 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, good point
LGTM |
$ python remove-idle-users.py
--token=someaccesstoken23478586496849569
--alias='#somealias:some.domain'
--homeserver=http://some.domain
--domain=some.domain
--since=50
--template=@irc_$NICK