skeleton/skeleton.py

177 lines
5.0 KiB
Python
Raw Normal View History

2019-06-28 06:23:40 +00:00
#!/usr/bin/env python
# Asyncronoua IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
# skeleton.py
import asyncio
import logging
import logging.handlers
import os
import random
2019-06-28 06:23:40 +00:00
import time
##################################################
class config:
class connection:
server = 'irc.supernets.org'
port = 6697
ipv6 = False
ssl = True
ssl_verify = False
vhost = None
channel = '#dev'
key = None
modes = None
class cert:
file = None
password = None
class ident:
nickname = 'skeleton'
username = 'skeleton'
realname = 'acid.vegas/skeleton'
class login:
network = None
nickserv = None
operator = None
class settings:
admin = 'nick!user@host' # Must be in nick!user@host format (Wildcards accepted)
log = False
class throttle:
command = 3
message = 0.5
reconnect = 15
rejoin = 5
timeout = 15
##################################################
def ssl_ctx():
import ssl
ctx = ssl.create_default_context()
if not config.connection.ssl_verify:
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
if config.cert.file:
ctx.load_cert_chain(config.cert.file, password=config.cert.password)
return ctx
2019-06-28 06:23:40 +00:00
##################################################
2019-06-28 06:23:40 +00:00
class Command:
def join_channel(chan, key=None):
Command.raw(f'JOIN {chan} {key}') if key else Command.raw('JOIN ' + chan)
2019-06-28 06:23:40 +00:00
def mode(target, mode):
Command.raw(f'MODE {target} {mode}')
2019-06-28 06:23:40 +00:00
def nick(new_nick):
Command.raw('NICK ' + new_nick)
2019-06-28 06:23:40 +00:00
def raw(data):
Bot.writer.write(data[:510].encode('utf-8') + b'\r\n')
2019-06-28 06:23:40 +00:00
def sendmsg(target, msg):
Command.raw(f'PRIVMSG {target} :{msg}')
2019-06-28 06:23:40 +00:00
##################################################
2019-06-28 06:23:40 +00:00
class Event:
def connect():
if config.connection.modes:
Command.raw(f'MODE {config.ident.nickname} +{config.connection.modes}')
if config.login.nickserv:
Command.sendmsg('NickServ', f'IDENTIFY {config.ident.nickname} {config.login.nickserv}')
if config.login.operator:
Command.raw(f'OPER {config.ident.username} {config.login.operator}')
Command.join_channel(config.connection.channel, config.connection.key)
async def disconnect():
Bot.writer.close()
await bot.writer.wait_closed()
asyncio.sleep(config.throttle.reconnect)
def nick_in_use():
new_nick = 'a' + str(random.randint(1000,9999))
Command.nick(new_nick)
async def handler():
while not Bot.reader.at_eof():
2019-06-28 06:23:40 +00:00
try:
data = await Bot.reader.readline()
data = data.decode('utf-8').strip()
logging.info(data)
args = data.split()
if data.startswith('ERROR :Closing Link:'):
raise Exception('Connection has closed.')
elif data.startswith('ERROR :Reconnecting too fast, throttled.'):
raise Exception('Connection has closed. (throttled)')
elif args[0] == 'PING':
Command.raw('PONG ' + args[1][1:])
elif args[1] == '001': #RPL_WELCOME
Event.connect()
elif args[1] == '433': #ERR_NICKNAMEINUSE
Event.nick_in_use()
elif args[1] == 'KICK':
pass # handle kick
except (UnicodeDecodeError, UnicodeEncodeError):
pass
except:
logging.exception('Unknown error has occured!')
2019-06-28 06:23:40 +00:00
##################################################
class IrcBot:
def __init__(self):
self.options = {
'host' : config.connection.server,
'port' : config.connection.port,
'limit' : 1024,
'ssl' : ssl_ctx() if config.connection.ssl else None,
'family' : socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET,
'local_addr' : (config.connection.vhost, 0) if config.connection.vhost else None
}
self.reader, self.writer = (None, None)
async def connect(self):
try:
self.reader, self.writer = await asyncio.wait_for(asyncio.open_connection(**self.options), timeout=config.throttle.timeout)
if config.login.network:
Command.raw('PASS ' + config.login.network)
Command.raw(f'USER {config.ident.username} 0 * :{config.ident.realname}')
Command.raw('NICK ' + config.ident.nickname)
except:
logging.exception('Failed to connect to IRC server!')
else:
await Event.handler()
##################################################
if __name__ == '__main__':
if not os.path.exists('logs'):
os.makedirs('logs')
sh = logging.StreamHandler()
sh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(message)s', '%I:%M %p'))
if config.settings.log:
fh = logging.handlers.RotatingFileHandler('logs/debug.log', maxBytes=250000, backupCount=7, encoding='utf-8')
fh.setFormatter(logging.Formatter('%(asctime)s | %(levelname)9s | %(filename)s.%(funcName)s.%(lineno)d | %(message)s', '%Y-%m-%d %I:%M %p'))
logging.basicConfig(level=logging.NOTSET, handlers=(sh,fh))
del fh,sh
else:
logging.basicConfig(level=logging.NOTSET, handlers=(sh,))
del sh
print('#'*56)
print('#{:^54}#'.format(''))
print('#{:^54}#'.format('Asyncronous IRC Bot Skeleton'))
print('#{:^54}#'.format('Developed by acidvegas in Python'))
print('#{:^54}#'.format('https://acid.vegas/skeleton'))
print('#{:^54}#'.format(''))
print('#'*56)
Bot = IrcBot()
asyncio.run(Bot.connect())