Compare commits

...
Sign in to create a new pull request.

9 commits

Author SHA1 Message Date
Jonatan Pålsson
529bcbb003 Add commands support 2017-06-12 21:47:14 +02:00
Jonatan Pålsson
bf661d9577 Fix nick treates as channel issue
Since nickname changes apply server-wide, they were incorrectly treated
as their own channels (this was a bug) - fixed this, and nickname
changes are now logged in all channels the bot has joined.
2017-06-10 12:23:07 +02:00
Viktor Sjölind
96e6da2752 Refactor to run in both Python 2.7 and Python 3.6
Refactor to run in both Python 2.7 and Python 3.6. This is important as
python 2.7 is end of life.

I tried to edit as few things as possible in this commit to minimize
conflicts which means the code could be prettier.

Signed-off-by: Viktor Sjölind <viktor@sjolind.se>
2017-06-10 11:59:11 +02:00
Jonatan Pålsson
2125b2d739 Make flask thread a daemon
This means the flask thread will also exit when the main thread receives
a KeyboardInterrupt. This closes #12.
2017-06-10 11:24:21 +02:00
Jonatan Pålsson
924fad4320 Use ',' rather than 'and' in .select() queries
This closes #11
2017-06-10 11:22:54 +02:00
Jonatan Pålsson
6292af08e6 Add environment variables for config 2017-06-10 10:57:41 +02:00
Richard Pannek
0a6a0c4e6f Add db and seen_prs to gitignore 2017-06-09 16:06:55 +02:00
Richard Pannek
233519ac3a Merge pull request #9 from rpannek/readme
Change project name in README
2017-06-09 12:58:15 +02:00
Richard Pannek
63a2a7af31 Change project name in README 2017-06-09 12:56:48 +02:00
8 changed files with 178 additions and 99 deletions

3
.gitignore vendored
View file

@ -1 +1,4 @@
*.pyc *.pyc
irc.db
seen_prs

15
Db.py
View file

@ -23,11 +23,16 @@ class LogMessage(BaseModel):
message_type = CharField() message_type = CharField()
message = CharField() message = CharField()
class Quote(BaseModel):
datetime = DateTimeField()
author = CharField()
message = CharField()
def create_tables(): def create_tables():
database.connect() database.connect()
try: try:
database.create_tables([LogMessage, Day, Channel]) database.create_tables([Quote, LogMessage, Day, Channel])
except OperationalError: except OperationalError:
pass # Database already exists pass # Database already exists
@ -55,6 +60,12 @@ def add_log_message(channel, nickname, message_type, message = None):
message_type = message_type, message_type = message_type,
message = message) message = message)
def add_quote(author, message):
Quote.create(
author = author,
message = message,
datetime = datetime.datetime.now().strftime("%H:%m:%S"))
def show_all_messages(): def show_all_messages():
for message in LogMessage.select(): for message in LogMessage.select():
print "<%s> %s" % (message.nickname, message.message) print("<%s> %s" % (message.nickname, message.message))

View file

@ -1,21 +1,32 @@
LogBot 0.4.2 # smooth-operator
============
Written by Chris Oliver <chris@excid3.com> smooth-operator is a IRC bot which logs everything in a channel and offers more convinient things like notifications about new commits on GitHub, etc. For a roadmap please check the issues on GitHub.
Many thanks to Filip Slagter for his contributions. Originally this was written by Chris Oliver <chris@excid3.com> with contributions from Filip Slagter. Now it has diverged quite a lot.
Requirements ## Requirements
------------
LogBot shows logs using flask, and stores logs using peewee. Install these dependencies using ``pip``: smooth-operator shows logs using flask, and stores logs using peewee. Install these dependencies using ``pip``:
pip install flask peewee pip install flask peewee
Usage ## Usage
-----
LogBot requires Python 2. It is NOT compatible with Python 3.
Configuration is done inside logbot.py.
python logbot.py smooth-operator requires Python 2. It is NOT compatible with Python 3. Configuration is either done inside logbot.py, or using environment variables. The following environment variables are respected:
You can view logs on http://localhost:5000 - ``IRC_SERVER``: IRC server
- ``IRC_PORT``: IRC server port
- ``IRC_SERVER_PASS``: Password for IRC server, if any
- ``IRC_CHANNELS``: IRC channels to join, separated by ``,``
- ``IRC_NICK``: Nickname
- ``IRC_NICK_PASS``: Password to use when authenticating to nickserv, if any
The bot can be launched using:
python2 logbot.py
You can view the logs on http://localhost:5000
## License
This project is licensed under the GPLv2.

44
commands.py Normal file
View file

@ -0,0 +1,44 @@
from Db import *
from irclib import nm_to_n
import random
class Commands:
def __init__(self):
self.commands = {
"quote": self.cmd_quote,
"remember_quote": self.cmd_remember_quote
}
def process(self, c, e):
msg = e.arguments()[0]
if msg.startswith("!"):
cmd = msg.split("!")[1].split(" ")[0]
if cmd in self.commands:
msg = " ".join(msg.split(" ")[1:])
self.commands[cmd](c, msg, e.target(), nm_to_n(e.source()))
else:
print "Unknown command", cmd
def cmd_quote(self, c, msg, target, source):
replies = [
"%s once said \"%s\"",
"I head from %s that \"%s\"",
"A wise man (haha, just kidding, it was actually %s) once said \"%s\""
]
reply = lambda msg: c.privmsg(target, msg)
random_query = Quote.select().order_by(fn.Random())
try:
one_quote = random_query.get()
reply(random.choice(replies) % (one_quote.author, one_quote.message))
except: # No quotes
reply("I don't know ay quotes :(")
def cmd_remember_quote(self, c, msg, target, source):
reply = lambda msg: c.privmsg(target, msg)
if (len(msg) > 1):
add_quote(msg.split(" ")[0], ' '.join(msg.split(" ")[1:]))
reply("I'll try to remember that!")
else:
reply("I didn't get that :(")

View file

@ -25,7 +25,14 @@ write simpler bots.
""" """
import sys import sys
from UserDict import UserDict
# UserDict is moved to collections in Python3
# In order to support Python 2.7 this has to be imported
# in the following way
try:
from UserDict import UserDict
except ImportError:
from collections import UserDict
from irclib import SimpleIRCClient from irclib import SimpleIRCClient
from irclib import nm_to_n, irc_lower, all_events from irclib import nm_to_n, irc_lower, all_events
@ -160,7 +167,7 @@ class SingleServerIRCBot(SimpleIRCClient):
"""[Internal]""" """[Internal]"""
before = nm_to_n(e.source()) before = nm_to_n(e.source())
after = e.target() after = e.target()
for ch in self.channels.values(): for ch in list(self.channels.values()):
if ch.has_user(before): if ch.has_user(before):
ch.change_nick(before, after) ch.change_nick(before, after)
@ -177,7 +184,7 @@ class SingleServerIRCBot(SimpleIRCClient):
def _on_quit(self, c, e): def _on_quit(self, c, e):
"""[Internal]""" """[Internal]"""
nick = nm_to_n(e.source()) nick = nm_to_n(e.source())
for ch in self.channels.values(): for ch in list(self.channels.values()):
if ch.has_user(nick): if ch.has_user(nick):
ch.remove_user(nick) ch.remove_user(nick)
@ -283,8 +290,6 @@ class IRCDict:
del self.canon_keys[ck] del self.canon_keys[ck]
def __iter__(self): def __iter__(self):
return iter(self.data) return iter(self.data)
def __contains__(self, key):
return self.has_key(key)
def clear(self): def clear(self):
self.data.clear() self.data.clear()
self.canon_keys.clear() self.canon_keys.clear()
@ -294,15 +299,15 @@ class IRCDict:
import copy import copy
return copy.copy(self) return copy.copy(self)
def keys(self): def keys(self):
return self.data.keys() return list(self.data.keys())
def items(self): def items(self):
return self.data.items() return list(self.data.items())
def values(self): def values(self):
return self.data.values() return list(self.data.values())
def has_key(self, key): def has_key(self, key):
return irc_lower(key) in self.canon_keys return irc_lower(key) in self.canon_keys
def update(self, dict): def update(self, dict):
for k, v in dict.items(): for k, v in list(dict.items()):
self.data[k] = v self.data[k] = v
def get(self, key, failobj=None): def get(self, key, failobj=None):
return self.data.get(key, failobj) return self.data.get(key, failobj)
@ -322,16 +327,16 @@ class Channel:
def users(self): def users(self):
"""Returns an unsorted list of the channel's users.""" """Returns an unsorted list of the channel's users."""
return self.userdict.keys() return list(self.userdict.keys())
def opers(self): def opers(self):
"""Returns an unsorted list of the channel's operators.""" """Returns an unsorted list of the channel's operators."""
return self.operdict.keys() return list(self.operdict.keys())
def voiced(self): def voiced(self):
"""Returns an unsorted list of the persons that have voice """Returns an unsorted list of the persons that have voice
mode set in the channel.""" mode set in the channel."""
return self.voiceddict.keys() return list(self.voiceddict.keys())
def has_user(self, nick): def has_user(self, nick):
"""Check whether the channel has a user.""" """Check whether the channel has a user."""

View file

@ -207,8 +207,8 @@ class IRC:
incoming data, if there are any. If that seems boring, look incoming data, if there are any. If that seems boring, look
at the process_forever method. at the process_forever method.
""" """
sockets = map(lambda x: x._get_socket(), self.connections) sockets = [x._get_socket() for x in self.connections]
sockets = filter(lambda x: x != None, sockets) sockets = [x for x in sockets if x != None]
if sockets: if sockets:
(i, o, e) = select.select(sockets, [], [], timeout) (i, o, e) = select.select(sockets, [], [], timeout)
self.process_data(i) self.process_data(i)
@ -342,7 +342,7 @@ class Connection:
self.irclibobj = irclibobj self.irclibobj = irclibobj
def _get_socket(): def _get_socket():
raise IRCError, "Not overridden" raise IRCError("Not overridden")
############################## ##############################
### Convenience wrappers. ### Convenience wrappers.
@ -433,10 +433,10 @@ class ServerConnection(Connection):
self.socket.connect((self.server, self.port)) self.socket.connect((self.server, self.port))
if ssl: if ssl:
self.ssl = socket.ssl(self.socket) self.ssl = socket.ssl(self.socket)
except socket.error, x: except socket.error as x:
self.socket.close() self.socket.close()
self.socket = None self.socket = None
raise ServerConnectionError, "Couldn't connect to socket: %s" % x raise ServerConnectionError("Couldn't connect to socket: %s" % x)
self.connected = 1 self.connected = 1
if self.irclibobj.fn_to_add_socket: if self.irclibobj.fn_to_add_socket:
self.irclibobj.fn_to_add_socket(self.socket) self.irclibobj.fn_to_add_socket(self.socket)
@ -491,7 +491,7 @@ class ServerConnection(Connection):
new_data = self.ssl.read(2**14) new_data = self.ssl.read(2**14)
else: else:
new_data = self.socket.recv(2**14) new_data = self.socket.recv(2**14)
except socket.error, x: except socket.error as x:
# The server hung up. # The server hung up.
self.disconnect("Connection reset by peer") self.disconnect("Connection reset by peer")
return return
@ -500,14 +500,14 @@ class ServerConnection(Connection):
self.disconnect("Connection reset by peer") self.disconnect("Connection reset by peer")
return return
lines = _linesep_regexp.split(self.previous_buffer + new_data) lines = _linesep_regexp.split(self.previous_buffer + new_data.decode())
# Save the last, unfinished line. # Save the last, unfinished line.
self.previous_buffer = lines.pop() self.previous_buffer = lines.pop()
for line in lines: for line in lines:
if DEBUG: if DEBUG:
print "FROM SERVER:", line print("FROM SERVER:", line)
if not line: if not line:
continue continue
@ -561,7 +561,7 @@ class ServerConnection(Connection):
command = "privnotice" command = "privnotice"
for m in messages: for m in messages:
if type(m) is types.TupleType: if type(m) is tuple:
if command in ["privmsg", "pubmsg"]: if command in ["privmsg", "pubmsg"]:
command = "ctcp" command = "ctcp"
else: else:
@ -569,15 +569,15 @@ class ServerConnection(Connection):
m = list(m) m = list(m)
if DEBUG: if DEBUG:
print "command: %s, source: %s, target: %s, arguments: %s" % ( print("command: %s, source: %s, target: %s, arguments: %s" % (
command, prefix, target, m) command, prefix, target, m))
self._handle_event(Event(command, prefix, target, m)) self._handle_event(Event(command, prefix, target, m))
if command == "ctcp" and m[0] == "ACTION": if command == "ctcp" and m[0] == "ACTION":
self._handle_event(Event("action", prefix, target, m[1:])) self._handle_event(Event("action", prefix, target, m[1:]))
else: else:
if DEBUG: if DEBUG:
print "command: %s, source: %s, target: %s, arguments: %s" % ( print("command: %s, source: %s, target: %s, arguments: %s" % (
command, prefix, target, [m]) command, prefix, target, [m]))
self._handle_event(Event(command, prefix, target, [m])) self._handle_event(Event(command, prefix, target, [m]))
else: else:
target = None target = None
@ -595,8 +595,8 @@ class ServerConnection(Connection):
command = "umode" command = "umode"
if DEBUG: if DEBUG:
print "command: %s, source: %s, target: %s, arguments: %s" % ( print("command: %s, source: %s, target: %s, arguments: %s" % (
command, prefix, target, arguments) command, prefix, target, arguments))
self._handle_event(Event(command, prefix, target, arguments)) self._handle_event(Event(command, prefix, target, arguments))
def _handle_event(self, event): def _handle_event(self, event):
@ -660,7 +660,7 @@ class ServerConnection(Connection):
try: try:
self.socket.close() self.socket.close()
except socket.error, x: except socket.error as x:
pass pass
self.socket = None self.socket = None
self._handle_event(Event("disconnect", self.server, "", [message])) self._handle_event(Event("disconnect", self.server, "", [message]))
@ -743,7 +743,7 @@ class ServerConnection(Connection):
def part(self, channels, message=""): def part(self, channels, message=""):
"""Send a PART command.""" """Send a PART command."""
if type(channels) == types.StringType: if type(channels) == bytes:
self.send_raw("PART " + channels + (message and (" " + message))) self.send_raw("PART " + channels + (message and (" " + message)))
else: else:
self.send_raw("PART " + ",".join(channels) + (message and (" " + message))) self.send_raw("PART " + ",".join(channels) + (message and (" " + message)))
@ -782,15 +782,16 @@ class ServerConnection(Connection):
The string will be padded with appropriate CR LF. The string will be padded with appropriate CR LF.
""" """
if self.socket is None: if self.socket is None:
raise ServerNotConnectedError, "Not connected." raise ServerNotConnectedError("Not connected.")
try: try:
string += "\r\n"
if self.ssl: if self.ssl:
self.ssl.write(string + "\r\n") self.ssl.write(string.encode())
else: else:
self.socket.send(string + "\r\n") self.socket.send(string.encode())
if DEBUG: if DEBUG:
print "TO SERVER:", string print("TO SERVER:", string)
except socket.error, x: except socket.error as x:
# Ouch! # Ouch!
self.disconnect("Connection reset by peer.") self.disconnect("Connection reset by peer.")
@ -888,8 +889,8 @@ class DCCConnection(Connection):
self.passive = 0 self.passive = 0
try: try:
self.socket.connect((self.peeraddress, self.peerport)) self.socket.connect((self.peeraddress, self.peerport))
except socket.error, x: except socket.error as x:
raise DCCConnectionError, "Couldn't connect to socket: %s" % x raise DCCConnectionError("Couldn't connect to socket: %s" % x)
self.connected = 1 self.connected = 1
if self.irclibobj.fn_to_add_socket: if self.irclibobj.fn_to_add_socket:
self.irclibobj.fn_to_add_socket(self.socket) self.irclibobj.fn_to_add_socket(self.socket)
@ -913,8 +914,8 @@ class DCCConnection(Connection):
self.socket.bind((socket.gethostbyname(socket.gethostname()), 0)) self.socket.bind((socket.gethostbyname(socket.gethostname()), 0))
self.localaddress, self.localport = self.socket.getsockname() self.localaddress, self.localport = self.socket.getsockname()
self.socket.listen(10) self.socket.listen(10)
except socket.error, x: except socket.error as x:
raise DCCConnectionError, "Couldn't bind socket: %s" % x raise DCCConnectionError("Couldn't bind socket: %s" % x)
return self return self
def disconnect(self, message=""): def disconnect(self, message=""):
@ -930,7 +931,7 @@ class DCCConnection(Connection):
self.connected = 0 self.connected = 0
try: try:
self.socket.close() self.socket.close()
except socket.error, x: except socket.error as x:
pass pass
self.socket = None self.socket = None
self.irclibobj._handle_event( self.irclibobj._handle_event(
@ -947,8 +948,8 @@ class DCCConnection(Connection):
self.socket = conn self.socket = conn
self.connected = 1 self.connected = 1
if DEBUG: if DEBUG:
print "DCC connection from %s:%d" % ( print("DCC connection from %s:%d" % (
self.peeraddress, self.peerport) self.peeraddress, self.peerport))
self.irclibobj._handle_event( self.irclibobj._handle_event(
self, self,
Event("dcc_connect", self.peeraddress, None, None)) Event("dcc_connect", self.peeraddress, None, None))
@ -956,7 +957,7 @@ class DCCConnection(Connection):
try: try:
new_data = self.socket.recv(2**14) new_data = self.socket.recv(2**14)
except socket.error, x: except socket.error as x:
# The server hung up. # The server hung up.
self.disconnect("Connection reset by peer") self.disconnect("Connection reset by peer")
return return
@ -985,11 +986,11 @@ class DCCConnection(Connection):
target = None target = None
for chunk in chunks: for chunk in chunks:
if DEBUG: if DEBUG:
print "FROM PEER:", chunk print("FROM PEER:", chunk)
arguments = [chunk] arguments = [chunk]
if DEBUG: if DEBUG:
print "command: %s, source: %s, target: %s, arguments: %s" % ( print("command: %s, source: %s, target: %s, arguments: %s" % (
command, prefix, target, arguments) command, prefix, target, arguments))
self.irclibobj._handle_event( self.irclibobj._handle_event(
self, self,
Event(command, prefix, target, arguments)) Event(command, prefix, target, arguments))
@ -1009,8 +1010,8 @@ class DCCConnection(Connection):
if self.dcctype == "chat": if self.dcctype == "chat":
self.socket.send("\n") self.socket.send("\n")
if DEBUG: if DEBUG:
print "TO PEER: %s\n" % string print("TO PEER: %s\n" % string)
except socket.error, x: except socket.error as x:
# Ouch! # Ouch!
self.disconnect("Connection reset by peer.") self.disconnect("Connection reset by peer.")
@ -1181,10 +1182,6 @@ def mask_matches(nick, mask):
r = re.compile(mask, re.IGNORECASE) r = re.compile(mask, re.IGNORECASE)
return r.match(nick) return r.match(nick)
_special = "-[]\\`^{}"
nick_characters = string.ascii_letters + string.digits + _special
_ircstring_translation = string.maketrans(string.ascii_uppercase + "[]\\^",
string.ascii_lowercase + "{}|~")
def irc_lower(s): def irc_lower(s):
"""Returns a lowercased string. """Returns a lowercased string.
@ -1192,7 +1189,12 @@ def irc_lower(s):
The definition of lowercased comes from the IRC specification (RFC The definition of lowercased comes from the IRC specification (RFC
1459). 1459).
""" """
return s.translate(_ircstring_translation) s = s.lower()
s = s.replace("[", "{")
s = s.replace("]", "}")
s = s.replace("\\", "|")
s = s.replace("^", "~")
return s
def _ctcp_dequote(message): def _ctcp_dequote(message):
"""[Internal] Dequote a message according to CTCP specifications. """[Internal] Dequote a message according to CTCP specifications.
@ -1259,16 +1261,16 @@ def ip_numstr_to_quad(num):
"""Convert an IP number as an integer given in ASCII """Convert an IP number as an integer given in ASCII
representation (e.g. '3232235521') to an IP address string representation (e.g. '3232235521') to an IP address string
(e.g. '192.168.0.1').""" (e.g. '192.168.0.1')."""
n = long(num) n = int(num)
p = map(str, map(int, [n >> 24 & 0xFF, n >> 16 & 0xFF, p = list(map(str, list(map(int, [n >> 24 & 0xFF, n >> 16 & 0xFF,
n >> 8 & 0xFF, n & 0xFF])) n >> 8 & 0xFF, n & 0xFF]))))
return ".".join(p) return ".".join(p)
def ip_quad_to_numstr(quad): def ip_quad_to_numstr(quad):
"""Convert an IP address string (e.g. '192.168.0.1') to an IP """Convert an IP address string (e.g. '192.168.0.1') to an IP
number as an integer given in ASCII representation number as an integer given in ASCII representation
(e.g. '3232235521').""" (e.g. '3232235521')."""
p = map(long, quad.split(".")) p = list(map(int, quad.split(".")))
s = str((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) s = str((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
if s[-1] == "L": if s[-1] == "L":
s = s[:-1] s = s[:-1]
@ -1557,4 +1559,4 @@ protocol_events = [
"pong", "pong",
] ]
all_events = generated_events + protocol_events + numeric_events.values() all_events = generated_events + protocol_events + list(numeric_events.values())

View file

@ -59,6 +59,7 @@ from pullrequest import PullRequest
from Db import * from Db import *
from flask import * from flask import *
import threading import threading
from commands import Commands
pat1 = re.compile(r"(^|[\n ])(([\w]+?://[\w\#$%&~.\-;:=,?@\[\]+]*)(/[\w\#$%&~/.\-;:=,?@\[\]+]*)?)", re.IGNORECASE | re.DOTALL) pat1 = re.compile(r"(^|[\n ])(([\w]+?://[\w\#$%&~.\-;:=,?@\[\]+]*)(/[\w\#$%&~/.\-;:=,?@\[\]+]*)?)", re.IGNORECASE | re.DOTALL)
@ -79,12 +80,12 @@ def urlify2(value):
DEBUG = False DEBUG = False
# IRC Server Configuration # IRC Server Configuration
SERVER = "irc.freenode.net" SERVER = os.getenv("IRC_SERVER", "irc.freenode.net")
PORT = 6667 PORT = os.getenv("IRC_PORT", 6667)
SERVER_PASS = None SERVER_PASS = os.getenv("IRC_SERVER_PASS", None)
CHANNELS=["#pelux"] CHANNELS = os.getenv("IRC_CHANNELS", "#pelux").split(",")
NICK = "pelux" NICK = os.getenv("IRC_NICK", "pelux")
NICK_PASS = "" NICK_PASS = os.getenv("IRC_NICK_PASS", "")
# The local folder to save logs # The local folder to save logs
LOG_FOLDER = "/var/www/html/" LOG_FOLDER = "/var/www/html/"
@ -115,13 +116,13 @@ def search(channel = None, nickname = None):
try: try:
channel = Channel.get(Channel.name == channel) channel = Channel.get(Channel.name == channel)
messages = LogMessage.select() \ messages = LogMessage.select() \
.where(LogMessage.channel == channel and \ .where(LogMessage.channel == channel, \
LogMessage.message.contains(query)) LogMessage.message.contains(query))
except: except:
pass # No such channel pass # No such channel
elif nickname: elif nickname:
messages = LogMessage.select() \ messages = LogMessage.select() \
.where(LogMessage.nickname == nickname and \ .where(LogMessage.nickname == nickname, \
LogMessage.message.contains(query)) LogMessage.message.contains(query))
else: else:
messages = LogMessage.select() \ messages = LogMessage.select() \
@ -135,13 +136,13 @@ def search(channel = None, nickname = None):
@flaskapp.route("/channels/<channel>/") @flaskapp.route("/channels/<channel>/")
@flaskapp.route("/channels/<channel>/<day>/") @flaskapp.route("/channels/<channel>/<day>/")
def channel(channel, day = None, query = None): def channel(channel, day = None):
channel = Channel.get(Channel.name == channel) channel = Channel.get(Channel.name == channel)
if day: if day:
d = Day.get(Day.date == day) d = Day.get(Day.date == day)
messages = LogMessage.select() \ messages = LogMessage.select() \
.where(LogMessage.day == d and \ .where(LogMessage.day == d, \
LogMessage.channel == channel) LogMessage.channel == channel)
return render_template("messages.html", return render_template("messages.html",
@ -164,18 +165,17 @@ def channels():
def append_line(filename, line): def append_line(filename, line):
data = open(filename, "rb").readlines()[:-2] data = open(filename, "rb").readlines()[:-2]
data += [line, "\n", "\n</body>", "\n</html>"] data += [line.encode(), "\n".encode(), "\n</body>".encode(), "\n</html>".encode()]
write_lines(filename, data) write_lines(filename, data)
def write_lines(filename, lines): def write_lines(filename, lines):
f = open(filename, "wb") with open(filename, "wb") as f:
f.writelines(lines) f.writelines(lines)
f.close()
def write_string(filename, string): def write_string(filename, string):
f = open(filename, "wb") with open(filename, "wb") as f:
f.write(string) f.write(string.encode())
f.close()
color_pattern = re.compile(r'(\[\d{1,2}m)') color_pattern = re.compile(r'(\[\d{1,2}m)')
"Pattern that matches ANSI color codes and the text that follows" "Pattern that matches ANSI color codes and the text that follows"
@ -204,16 +204,17 @@ class Logbot(SingleServerIRCBot):
self.chans = [x.lower() for x in channels] self.chans = [x.lower() for x in channels]
self.set_ftp() self.set_ftp()
self.nick_pass = nick_pass self.nick_pass = nick_pass
self.commands = Commands()
print "Logbot %s" % __version__ print("Logbot %s" % __version__)
print "Connecting to %s:%i..." % (server, port) print("Connecting to %s:%i..." % (server, port))
print "Press Ctrl-C to quit" print("Press Ctrl-C to quit")
def quit(self): def quit(self):
self.connection.disconnect("Quitting...") self.connection.disconnect("Quitting...")
def color(self, user): def color(self, user):
return "#%s" % md5(user).hexdigest()[:6] return "#%s" % md5(user.encode()).hexdigest()[:6]
def set_ftp(self, ftp=None): def set_ftp(self, ftp=None):
self.ftp = ftp self.ftp = ftp
@ -223,6 +224,7 @@ class Logbot(SingleServerIRCBot):
if event_name == "nick": if event_name == "nick":
message = params["new"] message = params["new"]
target = params["chan"]
elif event_name == "kick": elif event_name == "kick":
message = "%s kicked %s from %s. Reason: %s" % (nm_to_n(params["kicker"]), message = "%s kicked %s from %s. Reason: %s" % (nm_to_n(params["kicker"]),
params["user"], params["channel"], params["reason"]) params["user"], params["channel"], params["reason"])
@ -257,7 +259,7 @@ class Logbot(SingleServerIRCBot):
def on_all_raw_messages(self, c, e): def on_all_raw_messages(self, c, e):
"""Display all IRC connections in terminal""" """Display all IRC connections in terminal"""
if DEBUG: print e.arguments()[0] if DEBUG: print(e.arguments()[0])
def on_welcome(self, c, e): def on_welcome(self, c, e):
"""Join channels after successful connection""" """Join channels after successful connection"""
@ -320,6 +322,7 @@ class Logbot(SingleServerIRCBot):
def on_pubmsg(self, c, e): def on_pubmsg(self, c, e):
# if e.arguments()[0].startswith(NICK): # if e.arguments()[0].startswith(NICK):
# c.privmsg(e.target(), self.format["help"]) # c.privmsg(e.target(), self.format["help"])
self.commands.process(c, e)
self.write_event("pubmsg", e) self.write_event("pubmsg", e)
def on_pubnotice(self, c, e): def on_pubnotice(self, c, e):
@ -327,7 +330,7 @@ class Logbot(SingleServerIRCBot):
def on_privmsg(self, c, e): def on_privmsg(self, c, e):
# c.privmsg(nm_to_n(e.source()), self.format["help"]) # c.privmsg(nm_to_n(e.source()), self.format["help"])
pas pass
def on_quit(self, c, e): def on_quit(self, c, e):
nick = nm_to_n(e.source()) nick = nm_to_n(e.source())
@ -340,7 +343,7 @@ class Logbot(SingleServerIRCBot):
self.write_event("topic", e) self.write_event("topic", e)
def connect_ftp(): def connect_ftp():
print "Using FTP %s..." % (FTP_SERVER) print("Using FTP %s..." % (FTP_SERVER))
f = ftplib.FTP(FTP_SERVER, FTP_USER, FTP_PASS) f = ftplib.FTP(FTP_SERVER, FTP_USER, FTP_PASS)
f.cwd(FTP_FOLDER) f.cwd(FTP_FOLDER)
return f return f
@ -350,6 +353,7 @@ def main():
create_tables() create_tables()
t = threading.Thread(target=flaskapp.run, kwargs={"host": "0.0.0.0"}) t = threading.Thread(target=flaskapp.run, kwargs={"host": "0.0.0.0"})
t.daemon = True
t.start() t.start()
# Create the logs directory # Create the logs directory
@ -368,7 +372,6 @@ def main():
except KeyboardInterrupt: except KeyboardInterrupt:
if FTP_SERVER: bot.ftp.quit() if FTP_SERVER: bot.ftp.quit()
bot.quit() bot.quit()
t.join()
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -69,7 +69,7 @@ class PullRequest:
for repo in self.repos: for repo in self.repos:
r = requests.get(repo["uri"]) r = requests.get(repo["uri"])
if r.status_code != 200: if r.status_code != 200:
print "Error fetching %s", repo["name"] print("Error fetching %s", repo["name"])
break break
prs = r.json() prs = r.json()
@ -83,4 +83,4 @@ class PullRequest:
if __name__ == "__main__": if __name__ == "__main__":
p = PullRequest() p = PullRequest()
for line in p.check_all(): for line in p.check_all():
print line["message"] print(line["message"])