Initial commit.

This commit is contained in:
Julien Palard 2023-04-05 23:17:15 +02:00
commit 31d56acd65
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
3 changed files with 194 additions and 0 deletions

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# HTTP to XMPP gateway
This is a dumb dumb gateay between HTTP and XMPP:
You POST some text over HTTP, text gets sent over XMPP.
Only one XMPP jid can receive message.
## Installation
pip install http-to-xmpp
## Setup
It can be configured either via environment variables:
```bash
$ export XMPP_DEST_JID=the_human_receiving_messages@the_server.org
$ export XMPP_JID=the_bot_account@the_server.org
$ export XMPP_PASSWORD=the_bot_password
$ http-to-xmpp
```
or via arguments, but beware of `ps` showing your password!
```bash
$ http-to-xmpp --xmpp-jid the_bot_account@the_server.org --xmpp-password the_bot_password --xmpp-dest-jid the_human_receiving_messages@the_server.org
```
HTTP host and port to listen to can be changed using `--http-host` and `--http-port`, they default to `localhost:1985`.
## Usage
You just have to send POST requests, to `/` on the given host:port
pair so by default, using curl, one can post messages using:
$ curl -XPOST -d Coucou localhost:1985

119
http_to_xmpp.py Normal file
View File

@ -0,0 +1,119 @@
"""HTTP to XMPP gateway."""
import argparse
import asyncio
import os
import signal
import socket
import sys
import aioxmpp
import aioxmpp.dispatcher
from aiohttp import web
__version__ = "0.1"
class XMPPClient:
def __init__(self, jid, password):
self.jid = jid
self.password = password
async def setup(self):
self.hostname = socket.gethostname()
self.client = aioxmpp.Client(
aioxmpp.JID.fromstr(self.jid), aioxmpp.make_security_layer(self.password)
)
self.client.start()
def send(self, msg: str, to: str):
message = aioxmpp.Message(
aioxmpp.MessageType.CHAT,
to=aioxmpp.JID.fromstr(to),
)
message.body[None] = msg
self.client.enqueue(message)
class HTTPServer:
def __init__(self, host, port):
self.host = host
self.port = port
async def setup(self):
self.app = web.Application()
self.app.add_routes([web.post("/", self.on_post)])
self.runner = web.AppRunner(self.app)
await self.runner.setup()
self.site = web.TCPSite(self.runner, self.host, self.port)
await self.site.start()
def forward_to(self, to_jid, xmpp_client):
self.to_jid = to_jid
self.xmpp_client = xmpp_client
async def on_post(self, request):
if not self.xmpp_client:
return
self.xmpp_client.send(await request.text(), self.to_jid)
return web.Response(text="")
def parse_args():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--http-host", default="localhost")
parser.add_argument("--http-port", default=1985, type=int)
parser.add_argument(
"--xmpp-jid",
default=os.environ.get("XMPP_JID"),
help="XMPP account of the bot."
"if not given it's read from XMPP_JID environment variable.",
)
parser.add_argument(
"--xmpp-password",
default=os.environ.get("XMPP_PASSWORD"),
help="XMPP password of the bot, "
"if not given it's read from XMPP_PASSWORD environment variable.",
)
parser.add_argument(
"--xmpp-dest-jid",
default=os.environ.get("XMPP_DEST_JID"),
help="XMPP account for the human to receive messages."
"if not given it's read from XMPP_DEST_JID environment variable.",
)
args = parser.parse_args()
for mandatory in "xmpp_password", "xmpp_jid", "xmpp_dest_jid":
if not getattr(args, mandatory):
print(
f"No {mandatory.replace('_', ' ')} found neither via "
f"--{mandatory.replace('_', '-')} neither via "
f"{mandatory.upper()} env var.",
file=sys.stderr,
)
sys.exit(1)
return args
async def amain():
args = parse_args()
http_server = HTTPServer(args.http_host, args.http_port)
await http_server.setup()
xmpp_client = XMPPClient(args.xmpp_jid, args.xmpp_password)
await xmpp_client.setup()
http_server.forward_to(args.xmpp_dest_jid, xmpp_client)
event = asyncio.Event()
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, event.set)
await event.wait()
def main():
asyncio.run(amain())
if __name__ == "__main__":
main()

35
pyproject.toml Normal file
View File

@ -0,0 +1,35 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "http-to-xmpp"
description = "Basic one way gate between HTTP and XMPP."
readme = "README.md"
license = {text = "MIT License"}
authors = [
{name = "Julien Palard", email = "julien@palard.fr"},
]
requires-python = ">= 3.7"
dependencies = [
"aiohttp",
"aioxmpp",
]
dynamic = ["version"]
[project.urls]
homepage = "https://git.afpy.org/mdk/http-to-xmpp"
[project.scripts]
http-to-xmpp = "http_to_xmpp:main"
[tool.setuptools]
py-modules = [
"http_to_xmpp",
]
include-package-data = false
[tool.setuptools.dynamic.version]
attr = "http_to_xmpp.__version__"
[tool.black]