Created
November 29, 2014 20:06
-
-
Save MasterofJOKers/034af7e3b324676f5b1d to your computer and use it in GitHub Desktop.
vanilla Minecraft server to IRC chat gateway
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Prerequisites: | |
* pyinotify (python-pyinotify on debian) | |
* irc (python-irc on debian (jessie and later - otherwise via virtualenv)) | |
* start the minecraft server like this: | |
$ echo > input.log | |
$ tail -f input.log 2>/dev/null | java -server -X bla -jar minecraft_server.jar nogui & | |
$ cat >> input.log | |
* stop minecraft like this: | |
stop | |
CTRL+C | |
Usage: | |
python gateway.py | |
Configuration: | |
Edit this file's configuration variables. You can find them at the | |
beginning of the file. They're these all uppercase variables. | |
""" | |
import codecs | |
import re | |
import ssl | |
import irc.client | |
import irc.connection | |
import pyinotify | |
# full path to minecraft's latest.log | |
LOG_PATH = "logs/latest.log" | |
# ful path to file piped into minecraft console's stdin | |
INPUT_PATH = "input.log" | |
# IRC config | |
IRC_HOST = "localhost" | |
IRC_PORT = 6667 | |
IRC_NICK = "gateway" | |
IRC_USE_SSL = False | |
IRC_CHANNEL = "#minegate" | |
IRC_CHANNEL_PW = "" | |
# settings this to `True` will only let leave, join and chat messages pass from | |
# minecraft to IRC | |
NO_SERVER_MESSAGES = True | |
# characters allowed in minecraft chat message | |
MINECRAFT_MAX_CHAT_CHARS = 100 | |
# will later be a `pyinotify.ThreadedNotifier` | |
notifier = None | |
# will later be an `irc.client.server()` to be used in `LogWatch` | |
server = None | |
# minecraft log message regex | |
log_re = re.compile(r'^\[[^\]]+\] \[Server thread/(?P<loglevel>[^\]]+)\]: (?P<msg>.*)$') | |
class LogWatch(pyinotify.ProcessEvent): | |
def my_init(self): | |
"""Open log file and read all old lines | |
We'll open the log file here and read all the old lines once, so when | |
we're called on MODIFY only new lines will be read. | |
""" | |
self.log = codecs.open(LOG_PATH, 'r', 'utf-8') | |
self.log.readlines() | |
def process_IN_MODIFY(self, event): | |
"""write all new log lines to IRC | |
Setting NO_SERVER_MESSAGES will only write chat, join and leave | |
messages to IRC. | |
""" | |
data = self.log.readline() | |
while data != "": | |
match = log_re.match(data.strip()) | |
if not match: | |
print data.strip() | |
data = self.log.readline() | |
continue | |
print "%s: %s" % (match.group('loglevel'), match.group('msg')) | |
msg = match.group('msg') | |
# filter messages if necessary | |
if NO_SERVER_MESSAGES: | |
if match.group('loglevel') == 'INFO': | |
if (msg.startswith("<") or | |
msg.endswith("joined the game") or | |
msg.endswith("left the game")): | |
server.privmsg(IRC_CHANNEL, msg) | |
else: | |
if not (match.group('loglevel') == 'INFO' and msg.startswith("[Server]")): | |
server.privmsg(IRC_CHANNEL, msg) | |
data = self.log.readline() | |
def process_IN_CREATE(self, event): | |
"""reopen the logfile if it has been recreated | |
This happens if the minecraft server is restarted or if it rotates the | |
logfile. There is no need to read old lines, since the file should be | |
empty. | |
""" | |
if not self.log.closed: | |
self.log.close() | |
self.log = codecs.open(LOG_PATH, 'r', 'utf-8') | |
def IRCWatch(server, event): | |
"""Read an IRC message and write it to minecrafts console input""" | |
msg = event.arguments[0] | |
print msg | |
nick = "<%s> " % event.source.nick | |
while msg: | |
cmd = u"say %s%s" % (nick.encode('utf-8'), msg[:MINECRAFT_MAX_CHAT_CHARS-len(nick)]) | |
msg = msg[MINECRAFT_MAX_CHAT_CHARS-len(nick):] | |
f = codecs.open(INPUT_PATH, "a", 'utf-8') | |
f.write(cmd + "\n") | |
f.close() | |
def main(): | |
global notifier | |
global server | |
# pyinotify | |
# watch for MODIFY to geht new log lines and CREATE to reopen the LOG_PATH | |
# on logrotation | |
mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE | |
wm = pyinotify.WatchManager() | |
# we need a transient watch, because the server rotates its log | |
wm.watch_transient_file(LOG_PATH, mask, LogWatch) | |
notifier = pyinotify.ThreadedNotifier(wm, LogWatch()) | |
notifier.start() | |
# IRC | |
irc.client.ServerConnection.buffer_class.errors = 'replace' | |
client = irc.client.IRC() | |
server = client.server() | |
connect_args = { | |
"server": IRC_HOST, | |
"port": IRC_PORT, | |
"nickname": IRC_NICK | |
} | |
if IRC_USE_SSL: | |
ssl_factory = irc.connection.Factory(wrapper=ssl.wrap_socket) | |
connect_args['connect_factory'] = ssl_factory | |
server.connect(**connect_args) | |
server.join(IRC_CHANNEL, IRC_CHANNEL_PW) | |
client.add_global_handler("pubmsg", IRCWatch, 20) | |
client.process_forever() | |
if __name__ == '__main__': | |
try: | |
main() | |
except: | |
# also stop the started notifier thread | |
if notifier: | |
notifier.stop() | |
raise |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment