Difference between revisions of "Signal-IRC bridge"

From WTFwiki
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...")
 
Line 1: Line 1:
Quick and dirty hack. This allows me to chat on Signal using Minirc. :)
+
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 Minirc.
+
# 3) Connect to localhost:60667 with and IRC client.
  
 
import sys
 
import sys
Line 25: Line 25:
  
 
ircd = "signal-ircd.local"
 
ircd = "signal-ircd.local"
# Minimum needed to placate Minirc.
 
 
handshake = client_socket.recv(512).decode('utf-8')
 
handshake = client_socket.recv(512).decode('utf-8')
 
if not handshake.startswith('NICK '):
 
if not handshake.startswith('NICK '):
     sys.exit(f"Unexpected IRC handshake: {handshake}")
+
     # We don't care about USER.
 +
    handshake = client_socket.recv(512).decode('utf-8')
 +
    if not handshake.startswith('NICK '):
 +
        sys.exit(f"Unexpected IRC handshake: {handshake}")
 
nickname = handshake.split()[1]
 
nickname = handshake.split()[1]
 
def irc(action, message):
 
def irc(action, message):
Line 34: Line 36:
 
     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} :{message}\r\n'
+
     for m in message.split('\r\n'):
    client_socket.send(to_b.encode('utf-8'))
+
        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 66:
 
     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 73:
 
         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

Revision as of 16:48, 12 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 and 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')
if not handshake.startswith('NICK '):
    # We don't care about USER.
    handshake = client_socket.recv(512).decode('utf-8')
    if not handshake.startswith('NICK '):
        sys.exit(f"Unexpected IRC handshake: {handshake}")
nickname = handshake.split()[1]
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)