Skip to content

Instantly share code, notes, and snippets.

@MasterofJOKers
Created November 29, 2014 20:06
Show Gist options
  • Save MasterofJOKers/034af7e3b324676f5b1d to your computer and use it in GitHub Desktop.
Save MasterofJOKers/034af7e3b324676f5b1d to your computer and use it in GitHub Desktop.
vanilla Minecraft server to IRC chat gateway
"""
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