Signal-Messenger-Bot

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
JakobPrie
User
Beiträge: 91
Registriert: Sonntag 21. Juni 2020, 11:12

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
bmx
User
Beiträge: 1
Registriert: Freitag 1. November 2019, 08:58

Hallo @JakobPrie

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

Gruß,
Bernd
JakobPrie
User
Beiträge: 91
Registriert: Sonntag 21. Juni 2020, 11:12

Leider nicht, @bmx. Scheinbar gibt es da nichts gescheites :(
August1328
User
Beiträge: 65
Registriert: Samstag 27. Februar 2021, 12:18

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
nezzcarth
User
Beiträge: 1646
Registriert: Samstag 16. April 2011, 12:47

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).
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

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'}
Sirius3
User
Beiträge: 17823
Registriert: Sonntag 21. Oktober 2012, 17:20

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
Qubit
User
Beiträge: 128
Registriert: Dienstag 7. Oktober 2008, 09:07

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)

Antworten