aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdaemon.py131
1 files changed, 131 insertions, 0 deletions
diff --git a/daemon.py b/daemon.py
new file mode 100755
index 0000000..86db600
--- /dev/null
+++ b/daemon.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+"""Daemon connecting signal-cli and geldschieberbot
+
+The old approach of running signal-cli receive in a loop and passing its output
+to geldschieberbot is not really feasable to deploy as a service.
+"""
+
+import argparse
+import pathlib
+import json
+import os
+import subprocess
+import sys
+
+from geldschieberbot import Geldschieberbot, Reply
+
+
+class SignalCli:
+ """simple signal-cli jsonrpc wrapper
+
+ This wrapper manages the lifetime and handles th communication with
+ signal-cli.
+
+ The signal-cli JSON RPC service documentation is available at:
+ https://github.com/AsamK/signal-cli/wiki/JSON-RPC-service
+ """
+
+ def __init__(self, group_id='', user=''):
+ self.group_id = group_id
+ self.user = user
+ self.rpc_id = 0
+
+ signal_cli_cmd = 'signal-cli jsonRpc'
+ if self.user:
+ signal_cli_cmd += f' -u {user}'
+
+ self.signal = subprocess.Popen(signal_cli_cmd.split(),
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ text=True)
+
+ def send(self, msgs: list[Reply], group_id=''):
+ """Send one or more messages"""
+ print(msgs)
+ for msg in msgs:
+ call_id = self.rpc_id
+ self.rpc_id += 1
+
+ params = {'message': msg.msg}
+
+ if group_id:
+ params['groupId'] = group_id
+
+ if msg.attachment:
+ params['attachment'] = msg.attachment
+
+ if msg.quote:
+ params['quoteTimestamp'] = msg.quote.timestamp
+ params['quoteAuthor'] = msg.quote.author
+
+ method_call = {
+ 'jsonrpc': '2.0',
+ 'method': 'send',
+ 'id': call_id,
+ 'params': params
+ }
+
+ print(method_call)
+ json.dump(method_call, self.signal.stdin)
+
+ def receive(self) -> dict:
+ """Consumes all JSON RPC message until a recv notification is received"""
+ while True:
+ line = self.signal.stdout.readline()
+ data = json.loads(line)
+ # method call response
+ if 'id' in data:
+ assert data['id'] <= self.rpc_id
+ if 'error' in data:
+ print(
+ f'ERROR: {data["error"]["code"]} - {data["error"]["message"]}',
+ file=sys.stderr)
+ continue
+
+ if data['method'] == 'receive':
+ envelope = data['params']['envelope']
+ print(envelope)
+ return envelope
+
+
+def main():
+ """Main entry point connecting signal-cli geldschieberbot"""
+ # Load the environment
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-sf',
+ '--state',
+ help='the geldschieberbot state file to use')
+ parser.add_argument('-g',
+ '--group-id',
+ help='the signal group id to monitor')
+ args = parser.parse_args()
+
+ group_id = args.group_id or os.environ.get('GSB_GROUP_ID', None)
+ if not group_id:
+ print('ERROR: No group id provided', file=sys.stderr)
+ sys.exit(1)
+
+ _state_path = args.state or os.environ.get('GSB_STATE_FILE', None)
+ if not _state_path:
+ print('WARNING: No state file provided using "state.json"')
+ _state_path = 'state.json'
+ state_path = pathlib.Path(_state_path)
+
+ import pdb
+ # pdb.set_trace()
+ bot = Geldschieberbot(state_path)
+
+ # signal-cli init
+ signal_cli = SignalCli(group_id)
+
+ while True:
+ msg = signal_cli.receive()
+ if not 'dataMessage' in msg:
+ continue
+ signal_cli.send(bot.handle(msg), group_id=group_id)
+ signal_cli.send(bot.run_scheduled_cmds(), group_id=group_id)
+ bot.save_state()
+
+
+if __name__ == '__main__':
+ main()