-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrename-irc-nicks
More file actions
executable file
·181 lines (160 loc) · 7.27 KB
/
rename-irc-nicks
File metadata and controls
executable file
·181 lines (160 loc) · 7.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env python3
import re
import string
import time
import yaml
import requests
from unidecode import unidecode
from os import fdopen
import sys
# Line buffering, better for journals
sys.stdout = fdopen(sys.stdout.fileno(), 'w', buffering=1)
with open("config.yaml", 'r') as ymlfile:
cfg = yaml.load(ymlfile)
dry_run = False
homeserver = cfg['server']
## Take Matrix displayname and remove all invalid IRC characters
def ircify_displayname(displayname, bridge):
# Special cases: Whatsapp phone numbers and Discord nicknames with '#'
discord_antipattern = re.compile("#[0-9]{4}$")
tg_anon = re.compile("_telegram_")
whatsapp_fi_phone = re.compile("^whatsapp_358")
whatsapp_phone = re.compile("^whatsapp_")
ircified = re.sub(discord_antipattern, '', displayname)
ircified = re.sub(whatsapp_fi_phone, '|0', ircified)
ircified = re.sub(whatsapp_phone, '|', ircified)
ircified = re.sub(tg_anon, '|', ircified)
# Now making the whole string more friendly on IRC. First try transliteration
ircified = unidecode(ircified)
# Remove even some ascii chars which aro not allowed in IRC
allowed = string.ascii_letters+string.digits+'_-\[]{}^`|'
ircified = "".join(list(filter(allowed.__contains__, ircified)))
# Then, add prefix if still has illegal char at start
number_prefix = re.compile("^[-0-9]*")
ircified = re.sub(number_prefix, '', ircified)
# Finally, collect 12 first characters
ircified = ircified[:12]
ircified += bridge['irc_suffix']
return ircified
def process_network(bridge, network):
# find all rooms that have an alias matching given room format
rooms = requests.get(
"{}/_matrix/client/r0/joined_rooms".format(homeserver),
params={
'user_id': bridge['mxid'],
'access_token': bridge['token']
}).json()['joined_rooms']
print("Joined rooms: {}".format(rooms))
def is_irc_channel(members):
for mxid in members:
if mxid == network['appservice_user']:
return True
return False
# get all users in all those rooms that have IDs matching puppet user MXID regex
irc_users = {}
for room in rooms:
members = requests.get(
"{}/_matrix/client/r0/rooms/{}/joined_members".format(homeserver, room),
params={
'access_token': bridge['token']
}).json()['joined']
# Check first if this belongs to this IRC network
if not is_irc_channel(members):
continue
print("{} is an IRC channel".format(room))
for mxid, member in members.items():
if re.match(bridge['regex'], mxid):
irc_users[mxid] = member
print("IRC users: {}".format(irc_users))
## Change IRC nick:
def change_irc_nick(user, membership):
print("Changing IRC nick for {} {}".format(user, membership))
if membership['display_name'] is None:
print("Skipping {} because has no display name".format(user))
return
rooms = requests.get(
"{}/_matrix/client/r0/joined_rooms".format(homeserver),
params={
'access_token': bridge['token'],
'user_id': user
}).json()['joined_rooms']
def is_irc_admin_room(room_id, user):
members = requests.get(
"{}/_matrix/client/r0/rooms/{}/joined_members".format(homeserver, room_id),
params={
'access_token': bridge['token'],
'user_id': user
}).json()['joined']
return user in members and network['appservice_user'] in members and len(members)==2
irc_admin_rooms = [room for room in rooms if is_irc_admin_room(room, user)]
print("Previous IRC admin rooms for {}: {}".format(user, irc_admin_rooms))
if len(irc_admin_rooms) != 1:
for room in irc_admin_rooms:
# just leave all the previous admin rooms if they exist
requests.post(
"{}/_matrix/client/r0/rooms/{}/leave".format(homeserver, room),
params={
'access_token': bridge['token'],
'user_id': user
})
adminroom = requests.post(
"{}/_matrix/client/r0/createRoom".format(homeserver),
params={
'access_token': bridge['token'],
'user_id': user
},
# this payload is just copied straight from Riot
json={
'preset':'trusted_private_chat',
'visibility':'private',
'invite':[network['appservice_user']],
'is_direct':True,
'initial_state':[{
'content':{'guest_access':'can_join'},
'type':'m.room.guest_access',
'state_key':''
}]})
room_id = adminroom.json()['room_id']
else:
print("Reusing admin room")
room_id = irc_admin_rooms[0]
if dry_run:
ircified = ircify_displayname(membership['display_name'], bridge)
print("dry-run: would ircify {} to {}".format(membership['display_name'], ircified))
return # Skip actual renaming
time.sleep(2)
while network['appservice_user'] not in requests.get("{}/_matrix/client/r0/rooms/{}/joined_members".format(homeserver, room_id), params={'access_token': bridge['token'], 'user_id': user}).json()['joined']:
requests.post(
"{}/_matrix/client/r0/rooms/{}/invite".format(homeserver, room_id),
params={
'access_token': bridge['token'],
'user_id': user
},
json={
'user_id': network['appservice_user']
})
time.sleep(2)
txnid = str(time.time())
ircified = ircify_displayname(membership['display_name'], bridge)
print("ircifying {} to {}".format(membership['display_name'], ircified))
requests.put(
"{}/_matrix/client/r0/rooms/{}/send/m.room.message/{}".format(homeserver, room_id, txnid),
params={
'access_token': bridge['token'],
'user_id': user
},
json={
'msgtype': 'm.text',
'body': "!nick {}".format(ircified)
})
# for each of those users: change IRC nick to ircified displayname
for user, membership in irc_users.items():
try:
change_irc_nick(user, membership)
except KeyError as err:
print("Error changing nickname {0}. Error: {1}".format(user,err))
# Main loop: Iterate through every IRC network in all bridges
for bridge_name, bridge in cfg['bridge'].items():
for network_name, network in cfg['irc'].items():
print("Processing {} puppets on {}...".format(bridge_name, network_name))
process_network(bridge, network)