mirror of https://github.com/watcha-fr/synapse
parent
ba92c6f301
commit
f956646955
@ -1,154 +0,0 @@ |
||||
import curses |
||||
import curses.wrapper |
||||
from curses.ascii import isprint |
||||
|
||||
from twisted.internet import reactor |
||||
|
||||
|
||||
class CursesStdIO(): |
||||
def __init__(self, stdscr, callback=None): |
||||
self.statusText = "Synapse test app -" |
||||
self.searchText = '' |
||||
self.stdscr = stdscr |
||||
|
||||
self.logLine = '' |
||||
|
||||
self.callback = callback |
||||
|
||||
self._setup() |
||||
|
||||
def _setup(self): |
||||
self.stdscr.nodelay(1) # Make non blocking |
||||
|
||||
self.rows, self.cols = self.stdscr.getmaxyx() |
||||
self.lines = [] |
||||
|
||||
curses.use_default_colors() |
||||
|
||||
self.paintStatus(self.statusText) |
||||
self.stdscr.refresh() |
||||
|
||||
def set_callback(self, callback): |
||||
self.callback = callback |
||||
|
||||
def fileno(self): |
||||
""" We want to select on FD 0 """ |
||||
return 0 |
||||
|
||||
def connectionLost(self, reason): |
||||
self.close() |
||||
|
||||
def print_line(self, text): |
||||
""" add a line to the internal list of lines""" |
||||
|
||||
self.lines.append(text) |
||||
self.redraw() |
||||
|
||||
def print_log(self, text): |
||||
self.logLine = text |
||||
self.redraw() |
||||
|
||||
def redraw(self): |
||||
""" method for redisplaying lines |
||||
based on internal list of lines """ |
||||
|
||||
self.stdscr.clear() |
||||
self.paintStatus(self.statusText) |
||||
i = 0 |
||||
index = len(self.lines) - 1 |
||||
while i < (self.rows - 3) and index >= 0: |
||||
self.stdscr.addstr(self.rows - 3 - i, 0, self.lines[index], |
||||
curses.A_NORMAL) |
||||
i = i + 1 |
||||
index = index - 1 |
||||
|
||||
self.printLogLine(self.logLine) |
||||
|
||||
self.stdscr.refresh() |
||||
|
||||
def paintStatus(self, text): |
||||
if len(text) > self.cols: |
||||
raise RuntimeError("TextTooLongError") |
||||
|
||||
self.stdscr.addstr( |
||||
self.rows - 2, 0, |
||||
text + ' ' * (self.cols - len(text)), |
||||
curses.A_STANDOUT) |
||||
|
||||
def printLogLine(self, text): |
||||
self.stdscr.addstr( |
||||
0, 0, |
||||
text + ' ' * (self.cols - len(text)), |
||||
curses.A_STANDOUT) |
||||
|
||||
def doRead(self): |
||||
""" Input is ready! """ |
||||
curses.noecho() |
||||
c = self.stdscr.getch() # read a character |
||||
|
||||
if c == curses.KEY_BACKSPACE: |
||||
self.searchText = self.searchText[:-1] |
||||
|
||||
elif c == curses.KEY_ENTER or c == 10: |
||||
text = self.searchText |
||||
self.searchText = '' |
||||
|
||||
self.print_line(">> %s" % text) |
||||
|
||||
try: |
||||
if self.callback: |
||||
self.callback.on_line(text) |
||||
except Exception as e: |
||||
self.print_line(str(e)) |
||||
|
||||
self.stdscr.refresh() |
||||
|
||||
elif isprint(c): |
||||
if len(self.searchText) == self.cols - 2: |
||||
return |
||||
self.searchText = self.searchText + chr(c) |
||||
|
||||
self.stdscr.addstr(self.rows - 1, 0, |
||||
self.searchText + (' ' * ( |
||||
self.cols - len(self.searchText) - 2))) |
||||
|
||||
self.paintStatus(self.statusText + ' %d' % len(self.searchText)) |
||||
self.stdscr.move(self.rows - 1, len(self.searchText)) |
||||
self.stdscr.refresh() |
||||
|
||||
def logPrefix(self): |
||||
return "CursesStdIO" |
||||
|
||||
def close(self): |
||||
""" clean up """ |
||||
|
||||
curses.nocbreak() |
||||
self.stdscr.keypad(0) |
||||
curses.echo() |
||||
curses.endwin() |
||||
|
||||
|
||||
class Callback(object): |
||||
|
||||
def __init__(self, stdio): |
||||
self.stdio = stdio |
||||
|
||||
def on_line(self, text): |
||||
self.stdio.print_line(text) |
||||
|
||||
|
||||
def main(stdscr): |
||||
screen = CursesStdIO(stdscr) # create Screen object |
||||
|
||||
callback = Callback(screen) |
||||
|
||||
screen.set_callback(callback) |
||||
|
||||
stdscr.refresh() |
||||
reactor.addReader(screen) |
||||
reactor.run() |
||||
screen.close() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
curses.wrapper(main) |
@ -1,380 +0,0 @@ |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
""" This is an example of using the server to server implementation to do a |
||||
basic chat style thing. It accepts commands from stdin and outputs to stdout. |
||||
|
||||
It assumes that ucids are of the form <user>@<domain>, and uses <domain> as |
||||
the address of the remote home server to hit. |
||||
|
||||
Usage: |
||||
python test_messaging.py <port> |
||||
|
||||
Currently assumes the local address is localhost:<port> |
||||
|
||||
""" |
||||
|
||||
|
||||
from synapse.federation import ( |
||||
ReplicationHandler |
||||
) |
||||
|
||||
from synapse.federation.units import Pdu |
||||
|
||||
from synapse.util import origin_from_ucid |
||||
|
||||
from synapse.app.homeserver import SynapseHomeServer |
||||
|
||||
#from synapse.util.logutils import log_function |
||||
|
||||
from twisted.internet import reactor, defer |
||||
from twisted.python import log |
||||
|
||||
import argparse |
||||
import json |
||||
import logging |
||||
import os |
||||
import re |
||||
|
||||
import cursesio |
||||
import curses.wrapper |
||||
|
||||
|
||||
logger = logging.getLogger("example") |
||||
|
||||
|
||||
def excpetion_errback(failure): |
||||
logging.exception(failure) |
||||
|
||||
|
||||
class InputOutput(object): |
||||
""" This is responsible for basic I/O so that a user can interact with |
||||
the example app. |
||||
""" |
||||
|
||||
def __init__(self, screen, user): |
||||
self.screen = screen |
||||
self.user = user |
||||
|
||||
def set_home_server(self, server): |
||||
self.server = server |
||||
|
||||
def on_line(self, line): |
||||
""" This is where we process commands. |
||||
""" |
||||
|
||||
try: |
||||
m = re.match("^join (\S+)$", line) |
||||
if m: |
||||
# The `sender` wants to join a room. |
||||
room_name, = m.groups() |
||||
self.print_line("%s joining %s" % (self.user, room_name)) |
||||
self.server.join_room(room_name, self.user, self.user) |
||||
#self.print_line("OK.") |
||||
return |
||||
|
||||
m = re.match("^invite (\S+) (\S+)$", line) |
||||
if m: |
||||
# `sender` wants to invite someone to a room |
||||
room_name, invitee = m.groups() |
||||
self.print_line("%s invited to %s" % (invitee, room_name)) |
||||
self.server.invite_to_room(room_name, self.user, invitee) |
||||
#self.print_line("OK.") |
||||
return |
||||
|
||||
m = re.match("^send (\S+) (.*)$", line) |
||||
if m: |
||||
# `sender` wants to message a room |
||||
room_name, body = m.groups() |
||||
self.print_line("%s send to %s" % (self.user, room_name)) |
||||
self.server.send_message(room_name, self.user, body) |
||||
#self.print_line("OK.") |
||||
return |
||||
|
||||
m = re.match("^paginate (\S+)$", line) |
||||
if m: |
||||
# we want to paginate a room |
||||
room_name, = m.groups() |
||||
self.print_line("paginate %s" % room_name) |
||||
self.server.paginate(room_name) |
||||
return |
||||
|
||||
self.print_line("Unrecognized command") |
||||
|
||||
except Exception as e: |
||||
logger.exception(e) |
||||
|
||||
def print_line(self, text): |
||||
self.screen.print_line(text) |
||||
|
||||
def print_log(self, text): |
||||
self.screen.print_log(text) |
||||
|
||||
|
||||
class IOLoggerHandler(logging.Handler): |
||||
|
||||
def __init__(self, io): |
||||
logging.Handler.__init__(self) |
||||
self.io = io |
||||
|
||||
def emit(self, record): |
||||
if record.levelno < logging.WARN: |
||||
return |
||||
|
||||
msg = self.format(record) |
||||
self.io.print_log(msg) |
||||
|
||||
|
||||
class Room(object): |
||||
""" Used to store (in memory) the current membership state of a room, and |
||||
which home servers we should send PDUs associated with the room to. |
||||
""" |
||||
def __init__(self, room_name): |
||||
self.room_name = room_name |
||||
self.invited = set() |
||||
self.participants = set() |
||||
self.servers = set() |
||||
|
||||
self.oldest_server = None |
||||
|
||||
self.have_got_metadata = False |
||||
|
||||
def add_participant(self, participant): |
||||
""" Someone has joined the room |
||||
""" |
||||
self.participants.add(participant) |
||||
self.invited.discard(participant) |
||||
|
||||
server = origin_from_ucid(participant) |
||||
self.servers.add(server) |
||||
|
||||
if not self.oldest_server: |
||||
self.oldest_server = server |
||||
|
||||
def add_invited(self, invitee): |
||||
""" Someone has been invited to the room |
||||
""" |
||||
self.invited.add(invitee) |
||||
self.servers.add(origin_from_ucid(invitee)) |
||||
|
||||
|
||||
class HomeServer(ReplicationHandler): |
||||
""" A very basic home server implentation that allows people to join a |
||||
room and then invite other people. |
||||
""" |
||||
def __init__(self, server_name, replication_layer, output): |
||||
self.server_name = server_name |
||||
self.replication_layer = replication_layer |
||||
self.replication_layer.set_handler(self) |
||||
|
||||
self.joined_rooms = {} |
||||
|
||||
self.output = output |
||||
|
||||
def on_receive_pdu(self, pdu): |
||||
""" We just received a PDU |
||||
""" |
||||
pdu_type = pdu.pdu_type |
||||
|
||||
if pdu_type == "sy.room.message": |
||||
self._on_message(pdu) |
||||
elif pdu_type == "sy.room.member" and "membership" in pdu.content: |
||||
if pdu.content["membership"] == "join": |
||||
self._on_join(pdu.context, pdu.state_key) |
||||
elif pdu.content["membership"] == "invite": |
||||
self._on_invite(pdu.origin, pdu.context, pdu.state_key) |
||||
else: |
||||
self.output.print_line("#%s (unrec) %s = %s" % |
||||
(pdu.context, pdu.pdu_type, json.dumps(pdu.content)) |
||||
) |
||||
|
||||
#def on_state_change(self, pdu): |
||||
##self.output.print_line("#%s (state) %s *** %s" % |
||||
##(pdu.context, pdu.state_key, pdu.pdu_type) |
||||
##) |
||||
|
||||
#if "joinee" in pdu.content: |
||||
#self._on_join(pdu.context, pdu.content["joinee"]) |
||||
#elif "invitee" in pdu.content: |
||||
#self._on_invite(pdu.origin, pdu.context, pdu.content["invitee"]) |
||||
|
||||
def _on_message(self, pdu): |
||||
""" We received a message |
||||
""" |
||||
self.output.print_line("#%s %s %s" % |
||||
(pdu.context, pdu.content["sender"], pdu.content["body"]) |
||||
) |
||||
|
||||
def _on_join(self, context, joinee): |
||||
""" Someone has joined a room, either a remote user or a local user |
||||
""" |
||||
room = self._get_or_create_room(context) |
||||
room.add_participant(joinee) |
||||
|
||||
self.output.print_line("#%s %s %s" % |
||||
(context, joinee, "*** JOINED") |
||||
) |
||||
|
||||
def _on_invite(self, origin, context, invitee): |
||||
""" Someone has been invited |
||||
""" |
||||
room = self._get_or_create_room(context) |
||||
room.add_invited(invitee) |
||||
|
||||
self.output.print_line("#%s %s %s" % |
||||
(context, invitee, "*** INVITED") |
||||
) |
||||
|
||||
if not room.have_got_metadata and origin is not self.server_name: |
||||
logger.debug("Get room state") |
||||
self.replication_layer.get_state_for_context(origin, context) |
||||
room.have_got_metadata = True |
||||
|
||||
@defer.inlineCallbacks |
||||
def send_message(self, room_name, sender, body): |
||||
""" Send a message to a room! |
||||
""" |
||||
destinations = yield self.get_servers_for_context(room_name) |
||||
|
||||
try: |
||||
yield self.replication_layer.send_pdu( |
||||
Pdu.create_new( |
||||
context=room_name, |
||||
pdu_type="sy.room.message", |
||||
content={"sender": sender, "body": body}, |
||||
origin=self.server_name, |
||||
destinations=destinations, |
||||
) |
||||
) |
||||
except Exception as e: |
||||
logger.exception(e) |
||||
|
||||
@defer.inlineCallbacks |
||||
def join_room(self, room_name, sender, joinee): |
||||
""" Join a room! |
||||
""" |
||||
self._on_join(room_name, joinee) |
||||
|
||||
destinations = yield self.get_servers_for_context(room_name) |
||||
|
||||
try: |
||||
pdu = Pdu.create_new( |
||||
context=room_name, |
||||
pdu_type="sy.room.member", |
||||
is_state=True, |
||||
state_key=joinee, |
||||
content={"membership": "join"}, |
||||
origin=self.server_name, |
||||
destinations=destinations, |
||||
) |
||||
yield self.replication_layer.send_pdu(pdu) |
||||
except Exception as e: |
||||
logger.exception(e) |
||||
|
||||
@defer.inlineCallbacks |
||||
def invite_to_room(self, room_name, sender, invitee): |
||||
""" Invite someone to a room! |
||||
""" |
||||
self._on_invite(self.server_name, room_name, invitee) |
||||
|
||||
destinations = yield self.get_servers_for_context(room_name) |
||||
|
||||
try: |
||||
yield self.replication_layer.send_pdu( |
||||
Pdu.create_new( |
||||
context=room_name, |
||||
is_state=True, |
||||
pdu_type="sy.room.member", |
||||
state_key=invitee, |
||||
content={"membership": "invite"}, |
||||
origin=self.server_name, |
||||
destinations=destinations, |
||||
) |
||||
) |
||||
except Exception as e: |
||||
logger.exception(e) |
||||
|
||||
def paginate(self, room_name, limit=5): |
||||
room = self.joined_rooms.get(room_name) |
||||
|
||||
if not room: |
||||
return |
||||
|
||||
dest = room.oldest_server |
||||
|
||||
return self.replication_layer.paginate(dest, room_name, limit) |
||||
|
||||
def _get_room_remote_servers(self, room_name): |
||||
return [i for i in self.joined_rooms.setdefault(room_name,).servers] |
||||
|
||||
def _get_or_create_room(self, room_name): |
||||
return self.joined_rooms.setdefault(room_name, Room(room_name)) |
||||
|
||||
def get_servers_for_context(self, context): |
||||
return defer.succeed( |
||||
self.joined_rooms.setdefault(context, Room(context)).servers |
||||
) |
||||
|
||||
|
||||
def main(stdscr): |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('user', type=str) |
||||
parser.add_argument('-v', '--verbose', action='count') |
||||
args = parser.parse_args() |
||||
|
||||
user = args.user |
||||
server_name = origin_from_ucid(user) |
||||
|
||||
## Set up logging ## |
||||
|
||||
root_logger = logging.getLogger() |
||||
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(lineno)d - ' |
||||
'%(levelname)s - %(message)s') |
||||
if not os.path.exists("logs"): |
||||
os.makedirs("logs") |
||||
fh = logging.FileHandler("logs/%s" % user) |
||||
fh.setFormatter(formatter) |
||||
|
||||
root_logger.addHandler(fh) |
||||
root_logger.setLevel(logging.DEBUG) |
||||
|
||||
# Hack: The only way to get it to stop logging to sys.stderr :( |
||||
log.theLogPublisher.observers = [] |
||||
observer = log.PythonLoggingObserver() |
||||
observer.start() |
||||
|
||||
## Set up synapse server |
||||
|
||||
curses_stdio = cursesio.CursesStdIO(stdscr) |
||||
input_output = InputOutput(curses_stdio, user) |
||||
|
||||
curses_stdio.set_callback(input_output) |
||||
|
||||
app_hs = SynapseHomeServer(server_name, db_name="dbs/%s" % user) |
||||
replication = app_hs.get_replication_layer() |
||||
|
||||
hs = HomeServer(server_name, replication, curses_stdio) |
||||
|
||||
input_output.set_home_server(hs) |
||||
|
||||
## Add input_output logger |
||||
io_logger = IOLoggerHandler(input_output) |
||||
io_logger.setFormatter(formatter) |
||||
root_logger.addHandler(io_logger) |
||||
|
||||
## Start! ## |
||||
|
||||
try: |
||||
port = int(server_name.split(":")[1]) |
||||
except: |
||||
port = 12345 |
||||
|
||||
app_hs.get_http_server().start_listening(port) |
||||
|
||||
reactor.addReader(curses_stdio) |
||||
|
||||
reactor.run() |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
curses.wrapper(main) |
Loading…
Reference in new issue