prevent starting chat server more than once. Implement chat console in the cli
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package com.muwire.clilanterna
|
||||
|
||||
import com.googlecode.lanterna.gui2.TextBox
|
||||
import com.googlecode.lanterna.gui2.TextGUIThread
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.chat.ChatConnectionEvent
|
||||
import com.muwire.core.chat.ChatLink
|
||||
import com.muwire.core.chat.ChatMessageEvent
|
||||
import com.muwire.core.chat.UIConnectChatEvent
|
||||
|
||||
import net.i2p.data.DataHelper
|
||||
|
||||
class ChatConsoleModel {
|
||||
private final Core core
|
||||
private final TextGUIThread guiThread
|
||||
|
||||
volatile ChatLink link
|
||||
volatile Thread poller
|
||||
volatile boolean running
|
||||
|
||||
volatile TextBox textBox
|
||||
|
||||
|
||||
ChatConsoleModel(Core core, TextGUIThread guiThread) {
|
||||
this.core = core
|
||||
this.guiThread = guiThread
|
||||
}
|
||||
|
||||
void start() {
|
||||
if (running)
|
||||
return
|
||||
running = true
|
||||
core.chatServer.start()
|
||||
core.eventBus.with {
|
||||
register(ChatConnectionEvent.class, this)
|
||||
publish(new UIConnectChatEvent(host : core.me))
|
||||
}
|
||||
}
|
||||
|
||||
void onChatConnectionEvent(ChatConnectionEvent e) {
|
||||
if (e.persona != core.me)
|
||||
return // can't really happen
|
||||
|
||||
link = e.connection
|
||||
poller = new Thread({eventLoop()} as Runnable)
|
||||
poller.setDaemon(true)
|
||||
poller.start()
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running)
|
||||
return
|
||||
running = false
|
||||
core.chatServer.stop()
|
||||
poller?.interrupt()
|
||||
link = null
|
||||
}
|
||||
|
||||
private void eventLoop() {
|
||||
Thread.sleep(1000)
|
||||
while(running) {
|
||||
ChatLink link = this.link
|
||||
if (link == null || !link.isUp()) {
|
||||
Thread.sleep(100)
|
||||
continue
|
||||
}
|
||||
|
||||
Object event = link.nextEvent()
|
||||
if (event instanceof ChatMessageEvent)
|
||||
handleChatMessage(event)
|
||||
else if (event instanceof Persona)
|
||||
handleLeave(event)
|
||||
else
|
||||
throw new IllegalArgumentException("unknown event type $event")
|
||||
}
|
||||
}
|
||||
|
||||
private void handleChatMessage(ChatMessageEvent e) {
|
||||
String text = DataHelper.formatTime(e.timestamp)+" <"+e.sender.getHumanReadableName()+ "> ["+
|
||||
e.room+"] "+e.payload
|
||||
guiThread.invokeLater({textBox.addLine(text)})
|
||||
}
|
||||
|
||||
private void handleLeave(Persona p) {
|
||||
guiThread.invokeLater({textBox.addLine(p.getHumanReadableName()+ " disconnected")})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.muwire.clilanterna
|
||||
|
||||
import com.googlecode.lanterna.TerminalSize
|
||||
import com.googlecode.lanterna.gui2.BasicWindow
|
||||
import com.googlecode.lanterna.gui2.Button
|
||||
import com.googlecode.lanterna.gui2.GridLayout
|
||||
import com.googlecode.lanterna.gui2.GridLayout.Alignment
|
||||
import com.googlecode.lanterna.gui2.LayoutData
|
||||
import com.googlecode.lanterna.gui2.Panel
|
||||
import com.googlecode.lanterna.gui2.TextBox
|
||||
import com.googlecode.lanterna.gui2.TextGUI
|
||||
import com.googlecode.lanterna.gui2.Window
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.chat.ChatConnection
|
||||
import com.muwire.core.chat.ChatMessageEvent
|
||||
import com.muwire.core.chat.ChatServer
|
||||
|
||||
import net.i2p.data.DataHelper
|
||||
|
||||
class ChatConsoleView extends BasicWindow {
|
||||
private final TextGUI textGUI
|
||||
private final ChatConsoleModel model
|
||||
private final Core core
|
||||
|
||||
private final LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER, true, false)
|
||||
|
||||
private final TextBox textBox
|
||||
private final TextBox sayField
|
||||
|
||||
ChatConsoleView(Core core, ChatConsoleModel model, TextGUI textGUI, TerminalSize terminalSize) {
|
||||
super("Chat Server Console")
|
||||
this.core = core
|
||||
this.model = model
|
||||
this.textGUI = textGUI
|
||||
this.textBox = new TextBox(terminalSize,"", TextBox.Style.MULTI_LINE)
|
||||
model.textBox = textBox
|
||||
model.start()
|
||||
this.sayField = new TextBox("", TextBox.Style.SINGLE_LINE)
|
||||
|
||||
|
||||
setHints([Window.Hint.EXPANDED])
|
||||
|
||||
Panel contentPanel = new Panel()
|
||||
contentPanel.setLayoutManager(new GridLayout(1))
|
||||
contentPanel.addComponent(textBox, layoutData)
|
||||
|
||||
Panel bottomPanel = new Panel()
|
||||
bottomPanel.setLayoutManager(new GridLayout(5))
|
||||
|
||||
Button sayButton = new Button("Say",{say()})
|
||||
Button startButton = new Button("Start Server",{model.start()})
|
||||
Button stopButton = new Button("Stop Server", {model.stop()})
|
||||
Button closeButton = new Button("Close",{close()})
|
||||
|
||||
bottomPanel.with {
|
||||
addComponent(sayField,layoutData)
|
||||
addComponent(sayButton, layoutData)
|
||||
addComponent(startButton, layoutData)
|
||||
addComponent(stopButton, layoutData)
|
||||
addComponent(closeButton, layoutData)
|
||||
}
|
||||
contentPanel.addComponent(bottomPanel, layoutData)
|
||||
setComponent(contentPanel)
|
||||
}
|
||||
|
||||
private void say() {
|
||||
String command = sayField.getText()
|
||||
sayField.setText("")
|
||||
|
||||
UUID uuid = UUID.randomUUID()
|
||||
long now = System.currentTimeMillis()
|
||||
|
||||
String toAppend = DataHelper.formatTime(now) + " <" + core.me.getHumanReadableName() + "> [Console] " + command
|
||||
textBox.addLine(toAppend)
|
||||
|
||||
byte[] sig = ChatConnection.sign(uuid, now, ChatServer.CONSOLE, command, core.me, core.me, core.spk)
|
||||
|
||||
def event = new ChatMessageEvent( uuid : uuid,
|
||||
payload : command,
|
||||
sender : core.me,
|
||||
host : core.me,
|
||||
room : ChatServer.CONSOLE,
|
||||
chatTime : now,
|
||||
sig : sig
|
||||
)
|
||||
core.eventBus.publish(event)
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ class MainWindowView extends BasicWindow {
|
||||
private final UploadsModel uploadsModel
|
||||
private final FilesModel filesModel
|
||||
private final TrustModel trustModel
|
||||
private final ChatConsoleModel chatModel
|
||||
|
||||
private final Label connectionCount, incoming, outgoing
|
||||
private final Label known, failing, hopeless
|
||||
@@ -63,6 +64,9 @@ class MainWindowView extends BasicWindow {
|
||||
uploadsModel = new UploadsModel(textGUI.getGUIThread(), core, props)
|
||||
filesModel = new FilesModel(textGUI.getGUIThread(),core)
|
||||
trustModel = new TrustModel(textGUI.getGUIThread(), core)
|
||||
chatModel = new ChatConsoleModel(core, textGUI.getGUIThread())
|
||||
if (core.muOptions.startChatServer)
|
||||
core.chatServer.start()
|
||||
|
||||
setHints([Window.Hint.EXPANDED])
|
||||
Panel contentPanel = new Panel()
|
||||
@@ -74,7 +78,7 @@ class MainWindowView extends BasicWindow {
|
||||
Panel buttonsPanel = new Panel()
|
||||
contentPanel.addComponent(buttonsPanel, BorderLayout.Location.TOP)
|
||||
|
||||
GridLayout gridLayout = new GridLayout(7)
|
||||
GridLayout gridLayout = new GridLayout(8)
|
||||
buttonsPanel.setLayoutManager(gridLayout)
|
||||
|
||||
searchTextBox = new TextBox(new TerminalSize(40, 1))
|
||||
@@ -83,6 +87,7 @@ class MainWindowView extends BasicWindow {
|
||||
Button uploadsButton = new Button("Uploads", {upload()})
|
||||
Button filesButton = new Button("Files", { files() })
|
||||
Button trustButton = new Button("Trust", {trust()})
|
||||
Button chatButton = new Button("Chat", {chat()})
|
||||
Button quitButton = new Button("Quit", {close()})
|
||||
|
||||
LayoutData layoutData = GridLayout.createLayoutData(Alignment.CENTER, Alignment.CENTER)
|
||||
@@ -94,6 +99,7 @@ class MainWindowView extends BasicWindow {
|
||||
addComponent(uploadsButton, layoutData)
|
||||
addComponent(filesButton, layoutData)
|
||||
addComponent(trustButton, layoutData)
|
||||
addComponent(chatButton, layoutData)
|
||||
addComponent(quitButton, layoutData)
|
||||
}
|
||||
|
||||
@@ -271,6 +277,10 @@ class MainWindowView extends BasicWindow {
|
||||
textGUI.addWindowAndWait(new TrustView(trustModel, textGUI, core, sizeForTables()))
|
||||
}
|
||||
|
||||
private void chat() {
|
||||
textGUI.addWindowAndWait(new ChatConsoleView(core, chatModel, textGUI, sizeForTables()))
|
||||
}
|
||||
|
||||
private void refreshStats() {
|
||||
int inCon = 0
|
||||
int outCon = 0
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
package com.muwire.core.chat;
|
||||
|
||||
enum ChatAction {
|
||||
JOIN(true, false, true),
|
||||
LEAVE(false, false, true),
|
||||
SAY(false, false, true),
|
||||
LIST(true, true, true),
|
||||
HELP(true, true, true),
|
||||
INFO(true, true, true),
|
||||
JOINED(true, true, false);
|
||||
JOIN(true, false, true, false),
|
||||
LEAVE(false, false, true, false),
|
||||
SAY(false, false, true, false),
|
||||
LIST(true, true, true, false),
|
||||
HELP(true, true, true, false),
|
||||
INFO(true, true, true, false),
|
||||
JOINED(true, true, false, false),
|
||||
TRUST(true, false, true, true),
|
||||
DISTRUST(true, false, true, true);
|
||||
|
||||
final boolean console;
|
||||
final boolean stateless;
|
||||
final boolean user;
|
||||
ChatAction(boolean console, boolean stateless, boolean user) {
|
||||
final boolean local;
|
||||
ChatAction(boolean console, boolean stateless, boolean user, boolean local) {
|
||||
this.console = console;
|
||||
this.stateless = stateless;
|
||||
this.user = user;
|
||||
this.local = local;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class ChatServer {
|
||||
private final Map<Destination, ChatLink> connections = new ConcurrentHashMap()
|
||||
private final Map<String, Set<Persona>> rooms = new ConcurrentHashMap<>()
|
||||
private final Map<Persona, Set<String>> memberships = new ConcurrentHashMap<>()
|
||||
private final Map<String, Persona> shortNames = new ConcurrentHashMap<>()
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean()
|
||||
|
||||
@@ -49,9 +50,11 @@ class ChatServer {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
running.set(true)
|
||||
if (!running.compareAndSet(false, true))
|
||||
return
|
||||
connections.put(me.destination, LocalChatLink.INSTANCE)
|
||||
joinRoom(me, CONSOLE)
|
||||
shortNames.put(me.getHumanReadableName(), me)
|
||||
echo("/SAY Welcome to my chat server! Type /HELP for list of available commands.",me.destination)
|
||||
}
|
||||
|
||||
@@ -105,6 +108,7 @@ class ChatServer {
|
||||
ChatConnection connection = new ChatConnection(eventBus, endpoint, client, true, trustService, settings)
|
||||
connections.put(endpoint.destination, connection)
|
||||
joinRoom(client, CONSOLE)
|
||||
shortNames.put(client.getHumanReadableName(), client)
|
||||
connection.start()
|
||||
echo("/SAY Welcome to my chat server! Type /HELP for help on available commands",connection.endpoint.destination)
|
||||
}
|
||||
@@ -120,6 +124,7 @@ class ChatServer {
|
||||
leaveRoom(e.persona, it)
|
||||
}
|
||||
}
|
||||
shortNames.remove(e.persona.getHumanReadableName())
|
||||
connections.each { k, v ->
|
||||
v.sendLeave(e.persona)
|
||||
}
|
||||
@@ -187,6 +192,9 @@ class ChatServer {
|
||||
(!command.action.console && e.room == CONSOLE) ||
|
||||
!command.action.user)
|
||||
return
|
||||
|
||||
if (command.action.local && e.sender != me)
|
||||
return
|
||||
|
||||
switch(command.action) {
|
||||
case ChatAction.JOIN : processJoin(command.payload, e); break
|
||||
@@ -195,6 +203,8 @@ class ChatServer {
|
||||
case ChatAction.LIST : processList(e.sender.destination); break
|
||||
case ChatAction.INFO : processInfo(e.sender.destination); break
|
||||
case ChatAction.HELP : processHelp(e.sender.destination); break
|
||||
case ChatAction.TRUST : processTrust(command.payload, TrustLevel.TRUSTED); break
|
||||
case ChatAction.DISTRUST : processTrust(command.payload, TrustLevel.DISTRUSTED); break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,12 +274,14 @@ class ChatServer {
|
||||
|
||||
private void processHelp(Destination d) {
|
||||
String help = """/SAY
|
||||
Available commands: /JOIN /LEAVE /SAY /LIST /INFO /HELP
|
||||
Available commands: /JOIN /LEAVE /SAY /LIST /INFO /TRUST /DISTRUST /HELP
|
||||
/JOIN <room name> - joins a room, or creates one if it does not exist. You must type this in the console
|
||||
/LEAVE - leaves a room. You must type this in the room you want to leave
|
||||
/SAY - optional, says something in the room you're in
|
||||
/LIST - lists the existing rooms on this server. You must type this in the console
|
||||
/INFO - shows information about this server. You must type this in the console
|
||||
/TRUST <user> - marks user as trusted. This is only available to the server owner
|
||||
/DISTRUST <user> - marks user as distrusted. This is only available to the server owner
|
||||
/HELP - prints this help message
|
||||
"""
|
||||
echo(help, d)
|
||||
@@ -292,6 +304,13 @@ class ChatServer {
|
||||
connections[d]?.sendChat(echo)
|
||||
}
|
||||
|
||||
private void processTrust(String shortName, TrustLevel level) {
|
||||
Persona p = shortNames.get(shortName)
|
||||
if (p == null)
|
||||
return
|
||||
eventBus.publish(new TrustEvent(persona : p, level : level))
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (running.compareAndSet(true, false)) {
|
||||
connections.each { k, v ->
|
||||
|
||||
Reference in New Issue
Block a user