diff --git a/signal_bot/bot_function.py b/signal_bot/bot_function.py
new file mode 100644
index 000000000..c98fe6e3c
--- /dev/null
+++ b/signal_bot/bot_function.py
@@ -0,0 +1,22 @@
+import functions
+
+
+class SwitchCase(
+ functions.Help, functions.Bored, functions.Chuck, functions.Dog, functions.Flip, functions.Gif,
+ functions.Gnews, functions.Haiku, functions.Hn, functions.Me, functions.Rand, functions.Test,
+ functions.Trivia, functions.Twitch, functions.Version, functions.Winamp, functions.TestEmoji,
+ functions.Xkcd, functions.Tmdb
+):
+ """SwitchCase class to switch bot functions."""
+
+ def __init__(self, version, author, signalexecutorlocal, messageobject):
+ """Initialize SwitchCase with version, author, signalexecutor and the messagestring."""
+ self._version = version
+ self._author = author
+ self._signalexecutorlocal = signalexecutorlocal
+ self._messageobject = messageobject
+
+ def switch(self, action):
+ """Switch function to switch between available functions."""
+ default = "Invalid option."
+ return getattr(self, str(action)[1:].split()[0], lambda: default)()
\ No newline at end of file
diff --git a/signal_bot/main.py b/signal_bot/main.py
new file mode 100644
index 000000000..59a41105e
--- /dev/null
+++ b/signal_bot/main.py
@@ -0,0 +1,278 @@
+
+import os
+import pprint
+import json
+import time
+import docker
+import subprocess
+import shlex
+import logging
+import sys
+from distutils.util import strtobool
+from message import Message
+from botfunctions import SwitchCase
+from metadata import version, author
+
+__author__ = author
+__version__ = version
+SIGNALCLIIMAGE = "pblaas/signalcli:latest"
+
+logging.basicConfig(stream=sys.stdout, level=logging.INFO)
+
+if 'REGISTEREDNR' not in os.environ:
+ logging.error("Mandatory variable not set: REGISTEREDNR")
+ exit(1)
+else:
+ REGISTEREDNR = os.environ.get('REGISTEREDNR')
+
+if 'DEBUG' in os.environ:
+ DEBUG = bool(strtobool(os.environ.get('DEBUG')))
+else:
+ DEBUG = False
+
+if 'SIGNALEXECUTORLOCAL' in os.environ:
+ SIGNALEXECUTORLOCAL = bool(strtobool(os.environ.get('SIGNALEXECUTORLOCAL')))
+else:
+ SIGNALEXECUTORLOCAL = True
+
+if 'READY' in os.environ:
+ READY = bool(strtobool(os.environ.get('READY')))
+else:
+ READY = False
+
+if 'PRIVATECHAT' in os.environ:
+ PRIVATECHAT = bool(strtobool(os.environ.get('PRIVATECHAT')))
+else:
+ PRIVATECHAT = False
+
+if 'GROUPCHAT' in os.environ:
+ GROUPCHAT = bool(strtobool(os.environ.get('GROUPCHAT')))
+else:
+ GROUPCHAT = True
+
+if 'BLACKLIST' in os.environ:
+ blacklist = os.environ.get('BLACKLIST').split(',')
+else:
+ blacklist = []
+
+if 'WHITELIST' in os.environ:
+ whitelist = os.environ.get('WHITELIST').split(',')
+else:
+ whitelist = []
+
+
+def init_program():
+ """Initialize start of program."""
+ try:
+
+ homedir = os.environ['HOME']
+ if SIGNALEXECUTORLOCAL:
+ out = subprocess.run(["/signal/bin/signal-cli", "--config", "/config", "-o", "json", "-u", REGISTEREDNR, "receive"], stdout=subprocess.PIPE, text=True)
+ output = out.stdout
+ else:
+ dockerclient = docker.from_env()
+ out = dockerclient.containers.run(
+ SIGNALCLIIMAGE,
+ "-o json -u " + REGISTEREDNR + " receive",
+ auto_remove=True,
+ volumes={homedir + '/signal': {'bind': '/config', 'mode': 'rw'}}
+ )
+ output = out.decode('utf-8')
+ lines = []
+ for line in output.split("\n"):
+ lines.append(line)
+
+ for index, value in enumerate(lines):
+ if value:
+ parse_message(value.replace(u"\u2018", "'").replace(u"\u2019", "'"))
+
+ except docker.errors.NotFound:
+ logging.error("Unable to retrieve container. Please verify container.")
+ except docker.errors.APIError as e_error:
+ logging.error("Docker API error due to: " + e_error)
+
+
+def parse_message(value):
+ """Create message object from input."""
+ res = json.loads(value)
+ if DEBUG:
+ pprint.pprint(res)
+
+ # Messages send by the registered number itself.
+ if "syncMessage" in res['envelope']:
+ if "sentMessage" in res['envelope']['syncMessage']:
+ if "groupInfo" in res['envelope']['syncMessage']['sentMessage']:
+ messageobject = Message(
+ res['envelope']['source'],
+ res['envelope']['syncMessage']['sentMessage']['message'],
+ res['envelope']['syncMessage']['sentMessage']['groupInfo']['groupId'],
+ res['envelope']['syncMessage']['sentMessage']['timestamp']
+ )
+ if DEBUG:
+ logging.info(pprint.pprint(res))
+ logging.info(messageobject.getsource())
+ logging.info(messageobject.getgroupinfo())
+ logging.info(messageobject.getmessage())
+ if READY:
+ if group_not_in_blacklist(messageobject, blacklist) and group_in_whitelist(messageobject, whitelist):
+ run_signalcli(messageobject)
+ else:
+ logging.info("Group" + messageobject.getgroupinfo() + " is in the blacklist OR not in whitelist.")
+ else:
+ logging.info("NOOP due to ready mode set to false.")
+ if PRIVATECHAT and "groupInfo" not in res['envelope']['syncMessage']['sentMessage']:
+ messageobject = Message(
+ res['envelope']['source'],
+ res['envelope']['syncMessage']['sentMessage']['message'],
+ None,
+ res['envelope']['syncMessage']['sentMessage']['timestamp']
+ )
+ if DEBUG:
+ logging.info(pprint.pprint(res))
+ logging.info(messageobject.getsource())
+ logging.info(messageobject.getgroupinfo())
+ logging.info(messageobject.getmessage())
+ if READY:
+ run_signalcli(messageobject)
+ else:
+ logging.info("NOOP due to ready mode set to false.")
+
+ # Messages send by others.
+ if "dataMessage" in res['envelope']:
+ if "message" in res['envelope']['dataMessage']:
+ if "groupInfo" in res['envelope']['dataMessage']:
+ messageobject = Message(
+ res['envelope']['source'],
+ res['envelope']['dataMessage']['message'],
+ res['envelope']['dataMessage']['groupInfo']['groupId'],
+ res['envelope']['dataMessage']['timestamp']
+ )
+ if DEBUG:
+ logging.info(pprint.pprint(res))
+ logging.info(messageobject.getsource())
+ logging.info(messageobject.getgroupinfo())
+ logging.info(messageobject.getmessage())
+ if READY:
+ if group_not_in_blacklist(messageobject, blacklist) and group_in_whitelist(messageobject, whitelist):
+ run_signalcli(messageobject)
+ else:
+ logging.info("Group" + messageobject.getgroupinfo() + " is in the blacklist OR not in whitelist.")
+ else:
+ logging.info("NOOP due to ready mode set to false.")
+ if PRIVATECHAT and "groupInfo" not in res['envelope']['dataMessage']:
+ messageobject = Message(
+ res['envelope']['source'],
+ res['envelope']['dataMessage']['message'],
+ None,
+ res['envelope']['dataMessage']['timestamp']
+ )
+ if DEBUG:
+ logging.info(pprint.pprint(res))
+ logging.info(messageobject.getsource())
+ logging.info(messageobject.getgroupinfo())
+ logging.info(messageobject.getmessage())
+ if READY:
+ run_signalcli(messageobject)
+ else:
+ logging.info("NOOP due to ready mode set to false.")
+
+
+def run_signalcli(messageobject):
+ """Collect variables which should be passes to the cli send command."""
+ global client, home
+ if isinstance(messageobject.getmessage(), str) and messageobject.getmessage().startswith('!'):
+
+ action = SwitchCase(__version__, __author__, SIGNALEXECUTORLOCAL, messageobject.getmessage())
+ actionmessage = action.switch(messageobject.getmessage()).replace('"', '')
+
+ if not SIGNALEXECUTORLOCAL:
+ client = docker.from_env()
+ home = os.environ['HOME']
+ signal_cli_send(REGISTEREDNR, PRIVATECHAT, GROUPCHAT, SIGNALEXECUTORLOCAL, messageobject, actionmessage)
+
+
+def signal_cli_send(registerednr, privatechat, groupchat, signalexecutorlocal, messageobject, actionmessage):
+ """ Signal CLI command execution"""
+ localsignalcli = "/signal/bin/signal-cli --config /config "
+
+ if messageobject.getgroupinfo() is None and privatechat:
+ # this is a private one on one chat
+ if messageobject.getmessage() == "!gif" and actionmessage == "Gif":
+ target_param = " -u " + registerednr + " send " + messageobject.getsource() + " -a /tmp/signal/giphy.gif " + " -m "
+ elif messageobject.getmessage() == "!xkcd" and actionmessage == "xkcd":
+ target_param = " -u " + registerednr + " send " + messageobject.getsource() + " -a /tmp/signal/image.png " + " -m "
+ elif messageobject.getmessage() == "!me":
+ target_param = " -u " + registerednr + " sendReaction " + messageobject.getsource() + " -a " + messageobject.getsource() + " -t " + messageobject.gettimestamp() + " -e "
+ else:
+ target_param = " -u " + registerednr + " send " + messageobject.getsource() + " -m "
+
+ if signalexecutorlocal:
+ args = shlex.split(localsignalcli + target_param)
+ args.append(actionmessage)
+ subprocess.Popen(args)
+ else:
+ client.containers.run(
+ SIGNALCLIIMAGE,
+ target_param + "\"" + actionmessage + "\"",
+ auto_remove=True,
+ volumes={home + '/signal': {'bind': '/config', 'mode': 'rw'},
+ '/tmp/signal': {'bind': '/tmp/signal', 'mode': 'rw'}}
+ )
+ else:
+ if groupchat:
+ # this is a group chat
+ if messageobject.getmessage() == "!gif" and actionmessage == "Gif":
+ target_param = "-u " + registerednr + " send -g " + messageobject.getgroupinfo() + " -a /tmp/signal/giphy.gif " + " -m "
+ elif messageobject.getmessage() == "!xkcd" and actionmessage == "xkcd":
+ target_param = " -u " + registerednr + " send -g " + messageobject.getgroupinfo() + " -a /tmp/signal/image.png " + " -m "
+ elif messageobject.getmessage() == "!me":
+ target_param = " -u " + registerednr + " sendReaction " + "-g " + messageobject.getgroupinfo() + " -a " + messageobject.getsource() + " -t " + messageobject.gettimestamp() + " -e "
+ elif messageobject.getmessage() == "!rand":
+ target_param = "-u " + registerednr + " updateGroup -g " + messageobject.getgroupinfo() + " -n "
+ else:
+ target_param = "-u " + registerednr + " send -g " + messageobject.getgroupinfo() + " -m "
+
+ if signalexecutorlocal:
+ args = shlex.split(localsignalcli + target_param)
+ args.append(actionmessage)
+ subprocess.Popen(args)
+ else:
+ client.containers.run(
+ SIGNALCLIIMAGE,
+ target_param + "\"" + actionmessage + "\"",
+ auto_remove=True,
+ volumes={home + '/signal': {'bind': '/config', 'mode': 'rw'},
+ '/tmp/signal': {'bind': '/tmp/signal', 'mode': 'rw'}}
+ )
+
+
+def group_not_in_blacklist(messageobject, blist):
+ for groupid in blist:
+ if groupid == messageobject.getgroupinfo():
+ return False
+ return True
+
+
+def group_in_whitelist(messageobject, wlist):
+ if len(wlist) > 0:
+ for groupid in wlist:
+ if groupid == messageobject.getgroupinfo():
+ return True
+ return False
+ else:
+ return True
+
+
+if __name__ == '__main__':
+
+ logging.info("Signal bot " + __version__ + " started.")
+ logging.info("Debug " + str(DEBUG))
+ logging.info("Local Executor " + str(SIGNALEXECUTORLOCAL))
+ logging.info("Ready " + str(READY))
+ logging.info("Private chat " + str(PRIVATECHAT))
+ logging.info("Group Chat " + str(GROUPCHAT))
+ logging.info("Blacklists: " + str(blacklist))
+ logging.info("Whitelits: " + str(whitelist))
+ while True:
+ init_program()
+ time.sleep(2)
\ No newline at end of file
diff --git a/signal_bot/message.py b/signal_bot/message.py
new file mode 100644
index 000000000..163795e11
--- /dev/null
+++ b/signal_bot/message.py
@@ -0,0 +1,25 @@
+class Message:
+ """Message class to model data."""
+
+ def __init__(self, source, message, groupinfo, timestamp):
+ """Initialize Message object with source, message and groupinfo."""
+ self._source = source
+ self._message = message
+ self._groupinfo = groupinfo
+ self._timestamp = timestamp
+
+ def getgroupinfo(self):
+ """Return groupID."""
+ return self._groupinfo
+
+ def getmessage(self):
+ """Return message string."""
+ return self._message
+
+ def getsource(self):
+ """Return source string."""
+ return self._source
+
+ def gettimestamp(self):
+ """Return timestamp string."""
+ return str(self._timestamp)
\ No newline at end of file
diff --git a/signal_bot/modules.xml b/signal_bot/modules.xml
new file mode 100644
index 000000000..14c76987a
--- /dev/null
+++ b/signal_bot/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/signal_bot/pytest.ini b/signal_bot/pytest.ini
new file mode 100644
index 000000000..e8527dd4a
--- /dev/null
+++ b/signal_bot/pytest.ini
@@ -0,0 +1,5 @@
+[pytest]
+env_files =
+ .env
+ .test.env
+ .deploy.env
\ No newline at end of file
diff --git a/signal_bot/requirements.txt b/signal_bot/requirements.txt
new file mode 100644
index 000000000..afd0191f1
--- /dev/null
+++ b/signal_bot/requirements.txt
@@ -0,0 +1,55 @@
+//Installing and configuring signal-cli
+
+export VERSION=0.8.1
+wget https://github.com/USer/signal-cli/releases/download/v"${VERSION}"/signal-cli-"${VERSION}".tar.gz
+sudo tar xf signal-cli-"${VERSION}".tar.gz -C /opt
+sudo ln -sf /opt/signal-cli-"${VERSION}"/bin/signal-cli /user/local/bin/
+
+signal-cli -u +919452320987 register
+
+//Enable DBus service
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//Commands that can be run
+
+systemctl daemon-reload
+systemctl enable signal.service
+systemctl reload dbus.service
+
+//Configuring Python
+
+pip install git+https://github.com/LEW21/pydbus.git
+sudo apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0
+sudo apt install libgirepository1.0-dev gcc libcairo2-dev pkg-config python3-dev gir1.2-gtk-3.0
+pip install pycairo
+pip install PyGObject
+
+//Basic Requirements
+docker
+haikunator
+emoji
+urllib3
+pytest
+pytest-html
+pytest-cov
+pytest-dotenv
+coverage-badge
+six
\ No newline at end of file
diff --git a/signal_bot/setting.json b/signal_bot/setting.json
new file mode 100644
index 000000000..b23263be5
--- /dev/null
+++ b/signal_bot/setting.json
@@ -0,0 +1,15 @@
+{
+ "python.pythonPath": "/Users/pblaas/.pyenv/versions/signalbot-3.6.10/bin/python",
+ "python.linting.pylintEnabled": true,
+ "python.linting.pydocstyleEnabled": true,
+ "python.linting.enabled": true,
+ "python.linting.flake8Enabled": true,
+ "python.linting.pylintArgs": [
+ "--max-line-length=120",
+ "--disable=E501"
+ ],
+ "python.linting.flake8Args": [
+ "--max-line-length=120",
+ "--ignore=E501",
+ ],
+}
\ No newline at end of file
diff --git a/signal_bot/setup.py b/signal_bot/setup.py
new file mode 100644
index 000000000..02f2ac057
--- /dev/null
+++ b/signal_bot/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+from signalbot.metadata import version, author, authoremail
+
+setup(
+ name='SignalBot',
+ version=version,
+ description='Interactive bot for the Signal messenger platform',
+ packages=['signalbot'],
+ author=author,
+ author_email=authoremail,
+ license='MIT',
+ install_requires=['pytest', 'pytest-html', 'pytest-cov', 'pytest-dotenv', 'docker', 'haikunator', 'emoji', 'urllib3']
+)
\ No newline at end of file
diff --git a/signal_bot/signal.py b/signal_bot/signal.py
new file mode 100644
index 000000000..ddb4effc1
--- /dev/null
+++ b/signal_bot/signal.py
@@ -0,0 +1,16 @@
+from pydbus import SystemBus
+from gi.repository import GLib
+bus = SystemBus()
+loop = GLib.MainLoop()
+
+signal = bus.get('org.asamk.Signal', object_path='/org/asamk/Signal')
+
+def reply_ping(timestamp, source, groupID, message, attachments):
+ signal.sendMessage(message, [], [source])
+
+signal.onMessageReceived = reply_ping
+loop.run()
+
+//for debugging
+//ps aux | grep signal-cli
+kill PROCESS_ID
\ No newline at end of file
diff --git a/signal_bot/signalbot.iml.txt b/signal_bot/signalbot.iml.txt
new file mode 100644
index 000000000..8b8c39547
--- /dev/null
+++ b/signal_bot/signalbot.iml.txt
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file