diff --git a/syncplay/messages.py b/syncplay/messages.py index 4a4ae89..285589a 100644 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -110,6 +110,8 @@ en = { "server-messed-up-motd-too-long": "Message of the Day is too long - maximum of {} chars, {} given.", "server-http-reply-argument": "path to file from which http reply will be fetched", "server-default-http-reply": "This server should not be requested with your browser, but with Syncplay software available from http://syncplay.pl", + "server-irc-verbose": "Should server actively report changes in rooms", + "server-irc-config": "Path to irc bot config files", #Server errors "unknown-command-server-error" : "Unknown command {}", #message diff --git a/syncplay/server.py b/syncplay/server.py index a1a50cd..899bce4 100644 --- a/syncplay/server.py +++ b/syncplay/server.py @@ -12,9 +12,10 @@ from syncplay.messages import getMessage import codecs import os from string import Template +from ircBot import Bot as IRCBot class SyncFactory(Factory): - def __init__(self, password = '', motdFilePath = None, httpReplyFilePath= None): + def __init__(self, password = '', motdFilePath = None, httpReplyFilePath= None, ircConfig = None, ircVerbose = False): print getMessage("en", "welcome-server-notification").format(syncplay.version) if(password): password = hashlib.md5(password).hexdigest() @@ -24,6 +25,45 @@ class SyncFactory(Factory): self._rooms = {} self._roomStates = {} self._roomUpdate = threading.RLock() + self.ircVerbose = ircVerbose, + ircConnectionData = self.readIrcConfig(ircConfig) + if(ircConnectionData): + self.setupIRCBot(ircConnectionData) + + def readIrcConfig(self, ircConfig): #TODO: + if(ircConfig and os.path.isfile(ircConfig)): + cfg = codecs.open(ircConfig, "r", "utf-8-sig").read() + cfg = cfg.splitlines() + if(len(cfg) == 7): + ircConnectionData = {} + ircConnectionData['server'] = cfg[0] + ircConnectionData['serverPassword'] = cfg[1] + ircConnectionData['port'] = int(cfg[2]) + ircConnectionData['nick'] = cfg[3] + ircConnectionData['nickservPass'] = cfg[4] + ircConnectionData['channelPassword'] = cfg[5] + ircConnectionData['channel'] = cfg[6] + return ircConnectionData + + def setupIRCBot(self, ircConnectionData): + botFunctions = [ + self.ircPauseRoom, + self.getRooms, + self.getRoomPosition, + self.ircSetRoomPosition, + self.getRoomUsernames, + self.isRoomPaused, + ] + self.ircBot = IRCBot( + ircConnectionData['server'], + ircConnectionData['serverPassword'], + ircConnectionData['port'], + ircConnectionData['nick'], + ircConnectionData['nickservPass'], + ircConnectionData['channel'], + ircConnectionData['channelPassword'], + botFunctions, + ) def buildProtocol(self, addr): return SyncServerProtocol(self) @@ -54,6 +94,8 @@ class SyncFactory(Factory): reactor.callLater(0.1, watcher.scheduleSendState) l = lambda w: w.sendUserSetting(username, roomName, None, {"joined": True}) self.broadcast(watcherProtocol, l) + if(self.ircVerbose): + self.ircBot.sp_joined(username, roomName) def getWatcher(self, watcherProtocol): for room in self._rooms.itervalues(): @@ -157,6 +199,7 @@ class SyncFactory(Factory): self.__updateWatcherPing(latencyCalculation, watcher) watcher.lastUpdate = time.time() if(watcher.file): + oldPosition = self._roomStates[watcher.room]["position"] if(position is not None): self.__updatePositionState(position, doSeek, watcher) pauseChanged = False @@ -164,6 +207,13 @@ class SyncFactory(Factory): pauseChanged = self.__updatePausedState(paused, watcher) forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek) if(forceUpdate): + if(self.ircVerbose): + if(paused and pauseChanged): + self.ircBot.sp_paused(watcher.name, watcher.room) + elif(not paused and pauseChanged): + self.ircBot.sp_unpaused(watcher.name, watcher.room) + if(doSeek and position): + self.ircBot.sp_seek(watcher.name, watcher.room, oldPosition, position) l = lambda w: self.sendState(w, doSeek, watcher.latency, forceUpdate) self.broadcastRoom(watcher.watcherProtocol, l) @@ -177,7 +227,9 @@ class SyncFactory(Factory): watcher.deactivate() self._deleteRoomIfEmpty(watcher.room) print getMessage("en", "client-left-server-notification").format(watcher.name) - + if(self.ircVerbose): + self.ircBot.sp_left(watcher.name, watcher.room) + def watcherGetUsername(self, watcherProtocol): return self.getWatcher(watcherProtocol).name @@ -206,6 +258,8 @@ class SyncFactory(Factory): watcher.file = file_ l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None) self.broadcast(watcherProtocol, l) + if(self.ircVerbose): + self.ircBot.sp_fileplaying(watcher.name, watcher.file['name'], watcher.room) def broadcastRoom(self, sender, what): room = self._rooms[self.watcherGetRoom(sender)] @@ -220,6 +274,54 @@ class SyncFactory(Factory): for receiver in room: what(receiver) + def _findUserByUsername(self, username): + with self._roomUpdate: + for room in self._rooms.itervalues(): + for user in room.itervalues(): + if user.name == username: + return user + + def ircPauseRoom(self, setBy, paused): + user = self._findUserByUsername(setBy) + if(user): + with self._roomUpdate: + self._roomStates[user.room]['paused'] = paused + self._roomStates[user.room]['setBy'] = "IRC: " + setBy + l = lambda w: self.sendState(w, False, user.latency, True) + self.broadcastRoom(user.watcherProtocol, l) + + + def getRooms(self): + return self._rooms.keys() + + def getRoomPosition(self, room): + with self._roomUpdate: + if room in self._roomStates: + return self._roomStates[room]["position"] + + def ircSetRoomPosition(self, setBy, time): + user = self._findUserByUsername(setBy) + if(user): + with self._roomUpdate: + self._roomStates[user.room]['paused'] = time + self._roomStates[user.room]['setBy'] = "IRC: " + setBy + l = lambda w: self.sendState(w, True, user.latency, True) + self.broadcastRoom(user.watcherProtocol, l) + + + def getRoomUsernames(self, room): + l = [] + with self._roomUpdate: + if room in self._rooms: + for user in self._rooms[room].itervalues(): + l.append({'nick': user.name, 'file': user.file['name'], "length": user.file['duration']}) + return l + + def isRoomPaused(self, room): + with self._roomUpdate: + if room in self._roomStates: + return self._roomStates[room]["paused"] + class SyncIsolatedFactory(SyncFactory): def broadcast(self, sender, what): self.broadcastRoom(sender, what) diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index b87d30d..e2d8342 100644 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -208,3 +208,5 @@ class ServerConfigurationGetter(object): self._argparser.add_argument('--isolate-rooms', action='store_true', help=getMessage("en", "server-isolate-room-argument")) self._argparser.add_argument('--motd-file', metavar='file', type=str, nargs='?', help=getMessage("en", "server-motd-argument")) self._argparser.add_argument('--http-reply-file', metavar='file', type=str, nargs='?', help=getMessage("en", "server-http-reply-argument")) + self._argparser.add_argument('--irc-verbose', action='store_true', help=getMessage("en", "server-irc-verbose")) + self._argparser.add_argument('--irc-config-file', metavar='file', type=str, nargs='?', help=getMessage("en", "server-irc-config")) \ No newline at end of file diff --git a/syncplayServer.py b/syncplayServer.py index 23ff879..b8ecbd0 100644 --- a/syncplayServer.py +++ b/syncplayServer.py @@ -9,7 +9,7 @@ from syncplay.ui.ConfigurationGetter import ServerConfigurationGetter argsGetter = ServerConfigurationGetter() args = argsGetter.getConfiguration() if(not args.isolate_rooms): - reactor.listenTCP(int(args.port), SyncFactory(args.password, args.motd_file, args.http_reply_file)) + reactor.listenTCP(int(args.port), SyncFactory(args.password, args.motd_file, args.http_reply_file, args.irc_config_file, args.irc_verbose)) else: - reactor.listenTCP(int(args.port), SyncIsolatedFactory(args.password, args.motd_file, args.http_reply_file)) + reactor.listenTCP(int(args.port), SyncIsolatedFactory(args.password, args.motd_file, args.http_reply_file, args.irc_config_file, args.irc_verbose)) reactor.run()