diff options
| -rwxr-xr-x | daemon.py | 131 |
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() |
