Seite 1 von 1

Signal-Messenger-Bot

Verfasst: Donnerstag 4. Februar 2021, 23:51
von JakobPrie
Hallo,
ich möchte für den Messenger Signal einen Bot schreiben. Kennt jemand eine gescheite Api, worüber ich Nachrichten über Signal empfangen und versenden kann? Kann ich das auch ohne extra Handynummer?
Vielen Dank für Eure Hilfe!
Liebe Grüße,
Jakob

Re: Signal-Messenger-Bot

Verfasst: Sonntag 14. Mai 2023, 18:03
von bmx
Hallo @JakobPrie

bist Du fündig geworden bei Deiner Suche nach einer gescheiten API?

Gruß,
Bernd

Re: Signal-Messenger-Bot

Verfasst: Montag 12. Juni 2023, 08:01
von JakobPrie
Leider nicht, @bmx. Scheinbar gibt es da nichts gescheites :(

Re: Signal-Messenger-Bot

Verfasst: Dienstag 13. Juni 2023, 09:42
von August1328
Hallo zusammen,

da mich das Thema mittelfristig auch interessiert, habe ich mal kurz recherchiert...

Mein erster Weg führt mich bei solchen Themen immer zu Github und es gibt, wie erwartet, eine Github Seite von Signal: https://github.com/signalapp

OK, auf den ersten Blick nix in Python dabei, aber wenn man auf Github weiter sucht, findet man z.B. das hier, in Python geschrieben: https://github.com/filipre/signalbot
Am Ende der Seite gibt es auch links zu ähnlichen Projekten.

Und über eine große Suchmaschine findet man auch das: https://fabiobarbero.eu/posts/signalbot/

Für mich als leicht fortgeschrittenen Programmierer würde das für den Einstieg reichen.

Viel Erfolg & Grüße
Andy

Re: Signal-Messenger-Bot

Verfasst: Dienstag 13. Juni 2023, 16:31
von nezzcarth
Ich verwende(te) signal-cli in Python über 'subprocess', um mir von einem RaspberryPI aus Benachrichtigungen zu schicken. Das funktioniert auch so einigermaßen. Aber ob das "reaktionsfreudig" genug ist für einen Bot, der ein bisschen interaktiv sein soll, kann ich nicht sagen. Ich fand das alles eher etwas sperrig. Eine Telefonnummer wird auch benötigt und ich erinnere mich daran, dass die einmalige Einrichtung recht umständlich war (was aber auch am Raspi bzw. dem OS dort lag).

Re: Signal-Messenger-Bot

Verfasst: Freitag 16. Juni 2023, 11:15
von Qubit
An der "signal-cli" führt wohl offenbar (auf die Schnelle) kein Weg vorbei. Und die braucht ein Java-JDK.
Dann kann man aber z.B. signal-cli als Server auf localhost starten, mit TCP- oder Unix-Socket.

Code: Alles auswählen

signal-cli -u +491234567 daemon --tcp
In der "python-signal-cli-rest-api" findet man eine Vorlage für den Json-Client, den man sich anpassen und drumherum dann Funktionen zur Erzeugung der Json-Daten basteln kann (siehe API "signal-cli")..

Code: Alles auswählen

import socket
from json import dumps, loads
from uuid import uuid4

class JSONRPCResponseException(Exception):
    """
    JSONRPCResponseException
    """

def jsonrpc(data: dict, host: str = "localhost", port: int = 7583):
    """
    JSON RPC connection
    signal-cli -u +491234567 daemon --tcp
    """

    request_id = str(uuid4())
    data.update({"jsonrpc": "2.0", "id": request_id})
    recv_buffer = []

    if host.startswith("unix://"):
        sock_type = socket.AF_UNIX
        sock_conn = host.replace("unix://", "")
    else:
        sock_type = socket.AF_INET
        sock_conn = (host, port)

    with socket.socket(sock_type, socket.SOCK_STREAM) as sock:
        try:
            sock.connect(sock_conn)
            sock.settimeout(10)
            #logger.debug("JSON RPC request: %s", data)
            sock.sendall(dumps(data).encode("utf-8"))
            sock.shutdown(socket.SHUT_WR)
            while True:
                chunk = sock.recv(1)
                recv_buffer.append(chunk)
                if chunk == b"\n":
                    recv_content = b"".join(recv_buffer).decode("utf-8")
                    res = loads(recv_content)
                    #logger.debug("JSON RPC response: %s", recv_content)
                    recv_buffer = []
                    if res.get("id") == request_id:
                        if res.get("error"):
                            raise JSONRPCResponseException(
                                res.get("error").get("message")
                            )
                        return res
        except Exception as err:
            error = getattr(err, "message", repr(err))
            raise RuntimeError(f"signal-cli JSON RPC request failed: {error}") from err

#json_data ={"method": "version","params": {"account": "+491234567"}}
json_data ={"method": "version"}

res = jsonrpc(json_data).get('result')
print(res)
{'version': '0.11.11'}

Re: Signal-Messenger-Bot

Verfasst: Freitag 16. Juni 2023, 13:33
von Sirius3
Datenstrukturen die man einer Funktion übergibt, sollte man nicht ändern, wie es aber bei `data` der Fall ist. Variablen sollten erst dann initialisiert werden, wenn sie auch gebraucht werden und nicht schon 15 Zeilen vorher, wie bei `recv_buffer`.
Es ist sehr ineffizient, die Nachrichten in einzelnen Bytes zu lesen. Besser ist es, mit makefile zu arbeiten.
Das res.get innerhalb von JSONRPCResponseException ist falsch, da hier ja schon klar ist, dass es den Key "error" gibt. Je nachdem, wie das JSON-Format aufgebaut ist, sollte man die anderen get durch in-Vergleiche ersetzen.
getattr sollte man nicht verwenden. Die Exceptions, die in message-Attribut haben, müssen getrennt abgefangen werden.

Das ganze könnte also eher so aussehen:

Code: Alles auswählen

def jsonrpc(data, host="localhost", port=7583):
    """
    JSON RPC connection
    signal-cli -u +491234567 daemon --tcp
    """
    request_id = str(uuid4())
    data = dict(data, jsonrpc="2.0", id=request_id)

    if host.startswith("unix://"):
        family = socket.AF_UNIX
        address = host.replace("unix://", "")
    else:
        family = socket.AF_INET
        address = (host, port)

    with socket.socket(family, socket.SOCK_STREAM) as connection:
        try:
            connection.connect(address)
            connection.settimeout(10)
			file = connection.makefile('rwb')
			file.write(dumps(data).encode("utf-8"))
            connection.shutdown(socket.SHUT_WR)
			for line in file:
				result = loads(line)
				if result.get("id") == request_id:
					if result.get("error"):
						raise JSONRPCResponseException(
							result["error"].get("message")
						)
					return result
        except ExceptionWithMessage as err:
            raise RuntimeError(f"signal-cli JSON RPC request failed: {err.message}") from err
        except Exception as err:
            raise RuntimeError(f"signal-cli JSON RPC request failed") from err

Re: Signal-Messenger-Bot

Verfasst: Montag 19. Juni 2023, 23:00
von Qubit
Sirius3 hat geschrieben: Freitag 16. Juni 2023, 13:33 Das ganze könnte also eher so aussehen:
[...]
Vielen Dank für deine -wie immer- super Tipps! :cool:

Es fehlt aber offenbar noch ein flush im file-Objekt, oder?

Code: Alles auswählen

import socket, time
from json import dumps, loads
from uuid import uuid4

class ExceptionWithMessage(Exception):
    """
    JSONRPCResponseException
    """


def jsonrpc(data, host="localhost", port=7583):
    """
    JSON RPC connection
    signal-cli -u +491234567 daemon --tcp
    """
    request_id = str(uuid4())
    data = dict(data, jsonrpc="2.0", id=request_id)

    if host.startswith("unix://"):
        family = socket.AF_UNIX
        address = host.replace("unix://", "")
    else:
        family = socket.AF_INET
        address = (host, port)

    with socket.socket(family, socket.SOCK_STREAM) as connection:
        try:
            connection.connect(address)
            connection.settimeout(10)
            file = connection.makefile('rwb')
            file.write(dumps(data).encode("utf-8"))
            # >>>>>
            file.flush()
            # <<<<<
            connection.shutdown(socket.SHUT_WR)
            for line in file:
                result = loads(line)
                if result.get("id") == request_id:
                    if result.get("error"):
                        raise ExceptionWithMessage(
                            result["error"].get("message")
						)
                    return result
        except ExceptionWithMessage as err:
            raise RuntimeError(f"signal-cli JSON RPC request failed: {err.message}") from err
        except Exception as err:
            raise RuntimeError(f"signal-cli JSON RPC request failed") from err

json_data ={"method": "version"}

res = jsonrpc(json_data)
print(res)