Initial commit.
This commit is contained in:
commit
31d56acd65
|
@ -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
|
|
@ -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()
|
|
@ -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]
|
Loading…
Reference in New Issue