2016-01-25 11:07:39 +00:00
|
|
|
# An IRC bot named Flea
|
|
|
|
# Copyright (C) 2016 Kris Lamoureux
|
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
|
|
|
|
from core.config import *
|
2016-01-28 13:20:37 +00:00
|
|
|
import core.irclib as irclib
|
|
|
|
|
|
|
|
# Built-in to Python 2.7
|
2016-02-02 14:18:20 +00:00
|
|
|
import __builtin__
|
2016-01-28 07:54:43 +00:00
|
|
|
import socket
|
|
|
|
import ssl
|
2016-02-02 14:18:20 +00:00
|
|
|
import sys
|
|
|
|
import os
|
2016-02-03 23:14:45 +00:00
|
|
|
import re
|
2016-02-02 14:18:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Allows reimporting modules
|
|
|
|
class ImportRollback:
|
2016-03-01 07:51:50 +00:00
|
|
|
def __init__(self, plugins_folder="/plugins"):
|
2016-02-02 14:18:20 +00:00
|
|
|
# Dictionary of loaded modules
|
|
|
|
self.curMods = sys.modules.copy()
|
|
|
|
self.newImport = __builtin__.__import__
|
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
self.plugins = os.path.join(os.getcwd(), plugins_folder)
|
2016-02-02 14:18:20 +00:00
|
|
|
|
|
|
|
# Add the plugins location to the path variable
|
|
|
|
sys.path.append(self.plugins)
|
|
|
|
|
|
|
|
# Override builtin import function with install()
|
|
|
|
__builtin__.__import__ = self.install
|
|
|
|
self.newMods = {}
|
|
|
|
|
|
|
|
# Import modules
|
|
|
|
def install(self, mod, globals=None, locals=None, fromlist=[]):
|
|
|
|
self.newMods[mod] = 1
|
|
|
|
return apply(self.newImport, (mod, globals, locals, fromlist))
|
|
|
|
|
|
|
|
# Delete modules
|
|
|
|
def reset(self):
|
|
|
|
for mod in self.newMods.keys():
|
|
|
|
if not self.curMods.has_key(mod):
|
|
|
|
del(sys.modules[mod])
|
|
|
|
|
|
|
|
__builtin__.__import__ = self.newImport
|
|
|
|
|
|
|
|
|
2016-02-29 10:05:46 +00:00
|
|
|
# Print and log to logfile
|
2016-03-01 07:51:50 +00:00
|
|
|
def printlog(message, log=None):
|
2016-02-29 10:05:46 +00:00
|
|
|
print message
|
2016-03-01 07:51:50 +00:00
|
|
|
if log is not None:
|
|
|
|
log.write(message+"\n")
|
2016-02-29 10:05:46 +00:00
|
|
|
|
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
def PluginsImport(log=None, plugins_folder="/plugins"):
|
|
|
|
plugins = os.path.join(os.getcwd(), plugins_folder)
|
2016-02-03 23:14:45 +00:00
|
|
|
plugin_list = []
|
|
|
|
|
2016-02-02 14:18:20 +00:00
|
|
|
if os.path.exists(plugins):
|
|
|
|
os.chdir(plugins)
|
|
|
|
|
|
|
|
for item in os.listdir(plugins):
|
2016-03-01 07:51:50 +00:00
|
|
|
if os.path.isdir(os.path.join(plugins, item)):
|
|
|
|
printlog("[Plugins] Initializing " + item, log)
|
2016-02-03 23:14:45 +00:00
|
|
|
plugin = __import__(item+".main")
|
|
|
|
plugin_list.append(plugin)
|
2016-02-02 14:18:20 +00:00
|
|
|
|
|
|
|
else:
|
2016-03-01 07:51:50 +00:00
|
|
|
return None
|
2016-02-02 14:18:20 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
os.chdir(os.getcwd())
|
2016-02-03 23:14:45 +00:00
|
|
|
return plugin_list
|
|
|
|
|
2016-02-02 14:18:20 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
def init_connection(config_file="settings.conf"):
|
|
|
|
irc_conn = irclib.irc()
|
|
|
|
irc_conn.config = cfgParser(config_file)
|
2016-01-28 07:54:43 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
if irc_conn.config["logging"]:
|
2016-02-29 10:05:46 +00:00
|
|
|
log = open("log.txt", 'a')
|
2016-03-01 07:51:50 +00:00
|
|
|
irc_conn.log = log
|
2016-02-29 10:05:46 +00:00
|
|
|
else:
|
2016-03-01 07:51:50 +00:00
|
|
|
log = None
|
|
|
|
|
|
|
|
irc_conn.debug = irc_conn.config["debug"]
|
2016-02-29 10:05:46 +00:00
|
|
|
|
2016-02-02 14:18:20 +00:00
|
|
|
# Keep track of modules for a rollback
|
|
|
|
importctrl = ImportRollback()
|
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
# Import /plugins
|
|
|
|
if irc_conn.config["plugins"]:
|
2016-02-29 10:05:46 +00:00
|
|
|
plugins = PluginsImport(log)
|
2016-03-01 07:51:50 +00:00
|
|
|
else:
|
|
|
|
plugins = None
|
2016-02-29 10:05:46 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
if plugins is not None:
|
|
|
|
# TODO: Add more debugging messages, sporadatic.
|
|
|
|
printlog("[Plugins] Failed to load.", log)
|
2016-01-28 13:20:37 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
# Create socket object and wrap with SSL object, then connect.
|
|
|
|
irc_conn.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
irc_conn.sock = ssl.wrap_socket(irc_conn.sock)
|
2016-01-28 07:54:43 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
server = (irc_conn.config["host"], irc_conn.config["port"])
|
|
|
|
try:
|
|
|
|
printlog("Connecting to " + server[0] + ':' + str(server[1]))
|
|
|
|
irc_conn.sock.connect(server[0], server[1])
|
|
|
|
except:
|
|
|
|
printlog("Connection failed.", log)
|
|
|
|
return (None, None)
|
2016-01-28 07:54:43 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
# Display SSL inforation to the user
|
|
|
|
ssl_info = irc_conn.sock.cipher()
|
|
|
|
if ssl_info is not None:
|
|
|
|
printlog("[SSL] Cipher: " + ssl_info[0], log)
|
|
|
|
printlog("[SSL] Version: " + ssl_info[1], log)
|
|
|
|
printlog("[SSL] Bits: " + str(ssl_info[2]), log)
|
2016-02-29 10:05:46 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
# Establish identity on server
|
|
|
|
identity = (irc_conn.config["ident"], irc_conn.config["mode"], irc_conn.config["unused"], irc_conn.config["realname"])
|
|
|
|
irc_conn.User(identity)
|
|
|
|
irc_conn.Nick(irc_conn.config["nick"])
|
2016-01-28 07:54:43 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
return (irc_conn, plugins)
|
2016-01-28 07:54:43 +00:00
|
|
|
|
2016-01-28 13:20:37 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
def client_loop(irc_conn, plugins, log=None):
|
|
|
|
if irc_conn is None:
|
|
|
|
print "No connection established."
|
|
|
|
sys.exit(0)
|
2016-01-28 13:20:37 +00:00
|
|
|
|
2016-01-28 07:54:43 +00:00
|
|
|
while True:
|
|
|
|
# Buffer to store data from server
|
|
|
|
data = ''
|
|
|
|
|
|
|
|
while True:
|
|
|
|
# Receive data from connection
|
2016-03-01 07:51:50 +00:00
|
|
|
tmpdata = irc_conn.sock.recv(4096)
|
2016-01-28 07:54:43 +00:00
|
|
|
data = data + tmpdata
|
|
|
|
|
|
|
|
if len(tmpdata) < 4096:
|
|
|
|
break
|
2016-02-02 14:18:20 +00:00
|
|
|
|
2016-01-28 07:54:43 +00:00
|
|
|
# If no incoming data exists then connection has closed
|
|
|
|
if len(tmpdata) == 0:
|
2016-02-29 10:05:46 +00:00
|
|
|
print "Connection closed."
|
|
|
|
raw_input()
|
|
|
|
sys.exit(0)
|
2016-01-28 07:54:43 +00:00
|
|
|
|
|
|
|
# Split data to easily deal with it
|
|
|
|
data = tmpdata.split("\r\n")
|
2016-01-28 13:20:37 +00:00
|
|
|
|
|
|
|
# Parse IRC line by line
|
2016-01-28 07:54:43 +00:00
|
|
|
for line in data:
|
2016-01-28 13:20:37 +00:00
|
|
|
|
|
|
|
# Ignore empty lines
|
2016-01-28 07:54:43 +00:00
|
|
|
if len(line) > 0:
|
2016-01-28 13:20:37 +00:00
|
|
|
|
2016-02-29 10:05:46 +00:00
|
|
|
# Print/log line, parse it and respond
|
2016-03-01 07:51:50 +00:00
|
|
|
printlog(line, log)
|
|
|
|
irc_conn.pack = irc_conn.Parser(line)
|
2016-02-03 23:14:45 +00:00
|
|
|
|
|
|
|
# Run all plugins main() function
|
2016-02-29 10:05:46 +00:00
|
|
|
wait = ''
|
2016-03-01 07:51:50 +00:00
|
|
|
if irc_conn.config["plugins"]:
|
2016-02-03 23:14:45 +00:00
|
|
|
for plugin in plugins:
|
2016-03-01 07:51:50 +00:00
|
|
|
wait = plugin.main.main(irc_conn)
|
2016-02-29 10:05:46 +00:00
|
|
|
if wait == "QUIT":
|
|
|
|
break
|
2016-02-02 14:18:20 +00:00
|
|
|
|
2016-01-28 13:20:37 +00:00
|
|
|
# Ping Pong, keep the connection alive.
|
2016-03-01 07:51:50 +00:00
|
|
|
if irc_conn.pack["cmd"] == "PING":
|
|
|
|
irc_conn.Pong(irc_conn.pack["text"])
|
2016-01-28 13:20:37 +00:00
|
|
|
|
|
|
|
# Send user mode message after command 001
|
2016-03-01 07:51:50 +00:00
|
|
|
elif irc_conn.pack["cmd"] == "001":
|
|
|
|
irc_conn.Mode(irc_conn.config["nick"], irc_conn.config["mode"])
|
2016-01-28 13:20:37 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
elif irc_conn.pack["cmd"] == "NOTICE":
|
|
|
|
if irc_conn.pack["ident"] == "NickServ":
|
2016-02-02 14:18:20 +00:00
|
|
|
# Send password after NickServ informs you
|
|
|
|
# that your nick is registered
|
|
|
|
pattern = r"[Tt]his nickname is registered"
|
2016-03-01 07:51:50 +00:00
|
|
|
if re.search(pattern, irc_conn.pack["text"]):
|
|
|
|
irc_conn.Identify(irc_conn.config["password"])
|
|
|
|
irc_conn.Join(irc_conn.config["channel"])
|
2016-01-28 07:54:43 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
if log:
|
|
|
|
log.flush()
|
2016-02-29 10:05:46 +00:00
|
|
|
|
|
|
|
# Wait for QUIT to be returned from any plugin's main() function
|
|
|
|
if wait == "QUIT":
|
|
|
|
# Quit, close connection and logfile.
|
2016-03-01 07:51:50 +00:00
|
|
|
irc_conn.Quit("Fleabot https://github.com/Kris619/Flea")
|
|
|
|
irc_conn.sock.close()
|
|
|
|
if log:
|
|
|
|
log.close()
|
2016-02-29 10:05:46 +00:00
|
|
|
|
|
|
|
print "Press the [ENTER] key to close."
|
|
|
|
raw_input()
|
|
|
|
sys.exit(0)
|
|
|
|
|
2016-01-25 11:07:39 +00:00
|
|
|
|
2016-03-01 07:51:50 +00:00
|
|
|
def main():
|
|
|
|
(irc_conn, plugins) = init_connection()
|
|
|
|
client_loop(irc_conn, plugins)
|