Difference between revisions of "Signal-IRC bridge"
Jump to navigation
Jump to search
(Created page with "Quick and dirty hack. This allows me to chat on Signal using Minirc. :) <syntaxhighlight lang="python"> #!/usr/bin/env python3 # You need https://github.com/AsamK/signal-cli...") |
|||
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | Quick and dirty hack. This allows me to chat on Signal using | + | Quick and dirty hack. This allows me to chat on Signal using an IRC client. :) |
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
Line 9: | Line 9: | ||
# 1) Start signal-cli in dbus daemon mode: signal-cli -u <+phone> daemon | # 1) Start signal-cli in dbus daemon mode: signal-cli -u <+phone> daemon | ||
# 2) Start this thing. | # 2) Start this thing. | ||
− | # 3) Connect to localhost:60667 with | + | # 3) Connect to localhost:60667 with an IRC client. |
import sys | import sys | ||
Line 25: | Line 25: | ||
ircd = "signal-ircd.local" | ircd = "signal-ircd.local" | ||
− | |||
handshake = client_socket.recv(512).decode('utf-8') | handshake = client_socket.recv(512).decode('utf-8') | ||
− | + | while not 'NICK ' in handshake: | |
− | + | # We don't care about USER. | |
− | nickname = handshake.split()[ | + | handshake = client_socket.recv(512).decode('utf-8') |
+ | nickname = handshake[handshake.index('NICK '):].split('\r\n')[0] | ||
def irc(action, message): | def irc(action, message): | ||
to_b = f':{ircd} {action} {nickname} :{message}\r\n' | to_b = f':{ircd} {action} {nickname} :{message}\r\n' | ||
client_socket.send(to_b.encode('utf-8')) | client_socket.send(to_b.encode('utf-8')) | ||
irc('001', 'Signal-IRC bridge ready') | irc('001', 'Signal-IRC bridge ready') | ||
+ | irc('251', 'There are 1 users and 0 invisible on 1 servers') | ||
+ | irc('255', 'I have 1 clients and 1 servers') | ||
def ircmsg(source, message): | def ircmsg(source, message): | ||
− | to_b = f':{source}!signal@{ircd} PRIVMSG {nickname} :{ | + | for m in message.split('\r\n'): |
− | + | to_b = f':{source}!signal@{ircd} PRIVMSG {nickname} :{m}\r\n' | |
+ | client_socket.send(to_b.encode('utf-8')) | ||
# This can be prepopulated. | # This can be prepopulated. | ||
Line 61: | Line 64: | ||
if message == '': | if message == '': | ||
sys.exit("EOF from client, exiting") | sys.exit("EOF from client, exiting") | ||
− | lines = message.split('\n') | + | lines = message.split('\r\n') |
assert lines[-1] == '' and len(lines) == 2,\ | assert lines[-1] == '' and len(lines) == 2,\ | ||
f"If this fails we need more complex line handling: {lines}" | f"If this fails we need more complex line handling: {lines}" | ||
Line 68: | Line 71: | ||
challenge = message.split()[1] | challenge = message.split()[1] | ||
irc('PONG', challenge) | irc('PONG', challenge) | ||
+ | print("Pingpong") | ||
elif message.startswith('PRIVMSG '): | elif message.startswith('PRIVMSG '): | ||
recipient = message.split()[1] | recipient = message.split()[1] | ||
tonumber = signal_nick_map.get(recipient, recipient) | tonumber = signal_nick_map.get(recipient, recipient) | ||
− | signal_message = message.split(':')[1] | + | signal_message = message.split(':', 1)[1] |
signal.sendMessage(signal_message, [], [tonumber]) | signal.sendMessage(signal_message, [], [tonumber]) | ||
print(f"Sent to {tonumber}: {signal_message}") | print(f"Sent to {tonumber}: {signal_message}") | ||
else: | else: | ||
+ | irc('421', 'Unknown command') | ||
print(f"Unhandled IRC protocol message: {message}") | print(f"Unhandled IRC protocol message: {message}") | ||
return True | return True | ||
Line 87: | Line 92: | ||
except KeyboardInterrupt: | except KeyboardInterrupt: | ||
sys.exit(0) | sys.exit(0) | ||
+ | |||
</syntaxhighlight> | </syntaxhighlight> |
Latest revision as of 05:06, 14 October 2019
Quick and dirty hack. This allows me to chat on Signal using an IRC client. :)
#!/usr/bin/env python3
# You need https://github.com/AsamK/signal-cli installed and working and
# paired up to your phone before any of this can be used.
#
# 1) Start signal-cli in dbus daemon mode: signal-cli -u <+phone> daemon
# 2) Start this thing.
# 3) Connect to localhost:60667 with an IRC client.
import sys
import socket
from gi.repository import GLib
from pydbus import SessionBus
# For simplicity, accept just one client and set all the rest of it up after.
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('127.0.0.1', 60667))
server_socket.listen(0)
client_socket, client_address = server_socket.accept()
server_socket.close()
ircd = "signal-ircd.local"
handshake = client_socket.recv(512).decode('utf-8')
while not 'NICK ' in handshake:
# We don't care about USER.
handshake = client_socket.recv(512).decode('utf-8')
nickname = handshake[handshake.index('NICK '):].split('\r\n')[0]
def irc(action, message):
to_b = f':{ircd} {action} {nickname} :{message}\r\n'
client_socket.send(to_b.encode('utf-8'))
irc('001', 'Signal-IRC bridge ready')
irc('251', 'There are 1 users and 0 invisible on 1 servers')
irc('255', 'I have 1 clients and 1 servers')
def ircmsg(source, message):
for m in message.split('\r\n'):
to_b = f':{source}!signal@{ircd} PRIVMSG {nickname} :{m}\r\n'
client_socket.send(to_b.encode('utf-8'))
# This can be prepopulated.
signal_nick_map = {}
bus = SessionBus()
signal = bus.get('org.asamk.Signal')
def receive(timestamp, source, group_id, message, attachments):
print(f"Message from {source}: {message}")
name = signal.getContactName(source)
if name:
# Make phonebook name acceptable to IRC
fromnick = name.replace(' ', '_').replace(':', '')
if not fromnick in signal_nick_map:
signal_nick_map[fromnick] = source
ircmsg(fromnick, message)
else:
ircmsg(source, message)
return True
signal.onMessageReceived = receive
def transmit(channel, condition):
message = channel.read().decode('utf-8')
if message == '':
sys.exit("EOF from client, exiting")
lines = message.split('\r\n')
assert lines[-1] == '' and len(lines) == 2,\
f"If this fails we need more complex line handling: {lines}"
message = lines[0]
if message.startswith('PING '):
challenge = message.split()[1]
irc('PONG', challenge)
print("Pingpong")
elif message.startswith('PRIVMSG '):
recipient = message.split()[1]
tonumber = signal_nick_map.get(recipient, recipient)
signal_message = message.split(':', 1)[1]
signal.sendMessage(signal_message, [], [tonumber])
print(f"Sent to {tonumber}: {signal_message}")
else:
irc('421', 'Unknown command')
print(f"Unhandled IRC protocol message: {message}")
return True
socket_ch = GLib.IOChannel(filedes=client_socket.fileno())
socket_ch.set_flags(socket_ch.get_flags() | GLib.IOFlags.NONBLOCK)
GLib.io_add_watch(socket_ch, 0, GLib.IOCondition.IN, transmit)
loop = GLib.MainLoop()
try:
loop.run()
except KeyboardInterrupt:
sys.exit(0)