Initial commit
This commit is contained in:
commit
4a96baa378
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2019, acidvegas <acid.vegas@acid.vegas>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
12
README.md
Normal file
12
README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
###### Requirements
|
||||||
|
* [Python](https://www.python.org/downloads/) *(**Note:** This script was developed to be used with the latest version of Python)*
|
||||||
|
* [PySocks](https://pypi.python.org/pypi/PySocks) *(**Optional:** For using the `proxy` setting)*
|
||||||
|
|
||||||
|
###### IRC RCF Reference
|
||||||
|
- http://www.irchelp.org/protocol/rfc/
|
||||||
|
|
||||||
|
###### Mirrors
|
||||||
|
- [acid.vegas](https://acid.vegas/skeleton) *(main)*
|
||||||
|
- [SuperNETs](https://git.supernets.org/acidvegas/skeleton)
|
||||||
|
- [GitHub](https://github.com/acidvegas/skeleton)
|
||||||
|
- [GitLab](https://gitlab.com/acidvegas/skeleton)
|
275
skeleton.py
Normal file
275
skeleton.py
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
_connection = {'server':'irc.server.com', 'port':6697, 'proxy':None, 'ssl':True, 'ssl_verify':False, 'ipv6':False, 'vhost':None}
|
||||||
|
_cert = {'file':None, 'key':None, 'password':None}
|
||||||
|
_ident = {'nickname':'DevBot', 'username':'dev', 'realname':'acid.vegas/skeleton'}
|
||||||
|
_login = {'nickserv':None, 'network':None, 'operator':None}
|
||||||
|
_settings = {'channel':'#dev', 'key':None, 'modes':None, 'throttle':1}
|
||||||
|
|
||||||
|
# Formatting Control Characters / Color Codes
|
||||||
|
bold = '\x02'
|
||||||
|
italic = '\x1D'
|
||||||
|
underline = '\x1F'
|
||||||
|
reverse = '\x16'
|
||||||
|
reset = '\x0f'
|
||||||
|
white = '00'
|
||||||
|
black = '01'
|
||||||
|
blue = '02'
|
||||||
|
green = '03'
|
||||||
|
red = '04'
|
||||||
|
brown = '05'
|
||||||
|
purple = '06'
|
||||||
|
orange = '07'
|
||||||
|
yellow = '08'
|
||||||
|
light_green = '09'
|
||||||
|
cyan = '10'
|
||||||
|
light_cyan = '11'
|
||||||
|
light_blue = '12'
|
||||||
|
pink = '13'
|
||||||
|
grey = '14'
|
||||||
|
light_grey = '15'
|
||||||
|
|
||||||
|
def color(msg, foreground, background=None):
|
||||||
|
if background:
|
||||||
|
return f'\x03{foreground},{background}{msg}{reset}'
|
||||||
|
else:
|
||||||
|
return f'\x03{foreground}{msg}{reset}'
|
||||||
|
|
||||||
|
def debug(msg):
|
||||||
|
print(f'{get_time()} | [~] - {msg}')
|
||||||
|
|
||||||
|
def error(msg, reason=None):
|
||||||
|
if reason:
|
||||||
|
print(f'{get_time()} | [!] - {msg} ({reason})')
|
||||||
|
else:
|
||||||
|
print(f'{get_time()} | [!] - {msg}')
|
||||||
|
|
||||||
|
def error_exit(msg):
|
||||||
|
raise SystemExit(f'{get_time()} | [!] - {msg}')
|
||||||
|
|
||||||
|
def get_time():
|
||||||
|
return time.strftime('%I:%M:%S')
|
||||||
|
|
||||||
|
class IRC(object):
|
||||||
|
def __init__(self):
|
||||||
|
self._queue = list()
|
||||||
|
self._sock = None
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
Loop._loops()
|
||||||
|
self._connect()
|
||||||
|
|
||||||
|
def _connect(self):
|
||||||
|
try:
|
||||||
|
self._create_socket()
|
||||||
|
self._sock.connect((_connection['server'], _connection['port']))
|
||||||
|
self._register()
|
||||||
|
except socket.error as ex:
|
||||||
|
error('Failed to connect to IRC server.', ex)
|
||||||
|
Event._disconnect()
|
||||||
|
else:
|
||||||
|
self._listen()
|
||||||
|
|
||||||
|
def _create_socket(self):
|
||||||
|
family = socket.AF_INET6 if _connection['ipv6'] else socket.AF_INET
|
||||||
|
if _connection['proxy']:
|
||||||
|
proxy_server, proxy_port = _connection['proxy'].split(':')
|
||||||
|
self._sock = socks.socksocket(family, socket.SOCK_STREAM)
|
||||||
|
self._sock.setblocking(0)
|
||||||
|
self._sock.settimeout(15)
|
||||||
|
self._sock.setproxy(socks.PROXY_TYPE_SOCKS5, proxy_server, int(proxy_port))
|
||||||
|
else:
|
||||||
|
self._sock = socket.socket(family, socket.SOCK_STREAM)
|
||||||
|
if _connection['vhost']:
|
||||||
|
self._sock.bind((_connection['vhost'], 0))
|
||||||
|
if _connection['ssl']:
|
||||||
|
ctx = ssl.SSLContext()
|
||||||
|
if _cert['file']:
|
||||||
|
ctx.load_cert_chain(_cert['file'], _cert['key'], _cert['password'])
|
||||||
|
if _connection['ssl_verify']:
|
||||||
|
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||||
|
ctx.load_default_certs()
|
||||||
|
else:
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
self._sock = ctx.wrap_socket(self._sock)
|
||||||
|
|
||||||
|
def _listen(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data = self._sock.recv(1024).decode('utf-8')
|
||||||
|
for line in (line for line in data.split('\r\n') if len(line.split()) >= 2):
|
||||||
|
debug(line)
|
||||||
|
Event._handle(line)
|
||||||
|
except (UnicodeDecodeError,UnicodeEncodeError):
|
||||||
|
pass
|
||||||
|
except Exception as ex:
|
||||||
|
error('Unexpected error occured.', ex)
|
||||||
|
break
|
||||||
|
Event._disconnect()
|
||||||
|
|
||||||
|
def _register(self):
|
||||||
|
if _login['network']:
|
||||||
|
Bot._queue.append('PASS ' + _login['network'])
|
||||||
|
Bot._queue.append('USER {0} 0 * :{1}'.format(_ident['username'], _ident['realname']))
|
||||||
|
Bot._queue.append('NICK ' + _ident['nickname'])
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
def _action(target, msg):
|
||||||
|
Bot._queue.append(chan, f'\x01ACTION {msg}\x01')
|
||||||
|
|
||||||
|
def _ctcp(target, data):
|
||||||
|
Bot._queue.append(target, f'\001{data}\001')
|
||||||
|
|
||||||
|
def _invite(nick, chan):
|
||||||
|
Bot._queue.append(f'INVITE {nick} {chan}')
|
||||||
|
|
||||||
|
def _join(chan, key=None):
|
||||||
|
Bot._queue.append(f'JOIN {chan} {key}') if key else Bot._queue.append('JOIN ' + chan)
|
||||||
|
|
||||||
|
def _mode(target, mode):
|
||||||
|
Bot._queue.append(f'MODE {target} {mode}')
|
||||||
|
|
||||||
|
def _nick(nick):
|
||||||
|
Bot._queue.append('NICK ' + nick)
|
||||||
|
|
||||||
|
def _notice(target, msg):
|
||||||
|
Bot._queue.append(f'NOTICE {target} :{msg}')
|
||||||
|
|
||||||
|
def _part(chan, msg=None):
|
||||||
|
Bot._queue.append(f'PART {chan} {msg}') if msg else Bot._queue.append('PART ' + chan)
|
||||||
|
|
||||||
|
def _quit(msg=None):
|
||||||
|
Bot._queue.append('QUIT :' + msg) if msg else Bot._queue.append('QUIT')
|
||||||
|
|
||||||
|
def _raw(data):
|
||||||
|
Bot._sock.send(bytes(data[:510] + '\r\n', 'utf-8'))
|
||||||
|
|
||||||
|
def _sendmsg(target, msg):
|
||||||
|
Bot._queue.append(f'PRIVMSG {target} :{msg}')
|
||||||
|
|
||||||
|
def _topic(chan, text):
|
||||||
|
Bot._queue.append(f'TOPIC {chan} :{text}')
|
||||||
|
|
||||||
|
class Event:
|
||||||
|
def _connect():
|
||||||
|
if _settings['modes']:
|
||||||
|
Command._mode(_ident['nickname'], '+' + _settings['modes'])
|
||||||
|
if _login['nickserv']:
|
||||||
|
Command._sendmsg('NickServ', 'IDENTIFY {0} {1}'.format(_ident['nickname'], _login['nickserv']))
|
||||||
|
if _login['operator']:
|
||||||
|
Bot._queue.append('OPER {0} {1}'.format(_ident['username'], _login['operator']))
|
||||||
|
Command._join(_setting['channel'], _settings['key'])
|
||||||
|
|
||||||
|
def _ctcp(nick, chan, msg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _disconnect():
|
||||||
|
Bot._sock.close()
|
||||||
|
Bot._queue = list()
|
||||||
|
time.sleep(15)
|
||||||
|
Bot._connect()
|
||||||
|
|
||||||
|
def _invite(nick, chan):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _join(nick, chan):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _kick(nick, chan, kicked):
|
||||||
|
if kicked == _ident['nickname'] and chan == _settings['channel']:
|
||||||
|
time.sleep(3)
|
||||||
|
Command.join(chan, _Settings['key'])
|
||||||
|
|
||||||
|
def _message(nick, chan, msg):
|
||||||
|
if msg == '!test':
|
||||||
|
Bot._queue.append(chan, 'It Works!')
|
||||||
|
|
||||||
|
def _nick_in_use():
|
||||||
|
error_exit('The bot is already running or nick is in use!')
|
||||||
|
|
||||||
|
def _part(nick, chan):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _private(nick, msg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _quit(nick):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _handle(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':
|
||||||
|
Event._connect()
|
||||||
|
elif args[1] == '433':
|
||||||
|
Event._nick_in_use()
|
||||||
|
elif args[1] == 'INVITE':
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[3][1:]
|
||||||
|
Event._invite(nick, chan)
|
||||||
|
elif args[1] == 'JOIN':
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2][1:]
|
||||||
|
Event._join(nick, chan)
|
||||||
|
elif args[1] == 'KICK':
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2]
|
||||||
|
kicked = args[3]
|
||||||
|
Event._kick(nick, chan, kicked)
|
||||||
|
elif args[1] == 'PART':
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2]
|
||||||
|
Event._part(nick, chan)
|
||||||
|
elif args[1] == 'PRIVMSG':
|
||||||
|
#ident = args[0][1:]
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2]
|
||||||
|
msg = ' '.join(args[3:])[1:]
|
||||||
|
if msg.startswith('\001'):
|
||||||
|
Event._ctcp(nick, chan, msg)
|
||||||
|
elif chan == _ident['nickname']:
|
||||||
|
Event._private(nick, msg)
|
||||||
|
else:
|
||||||
|
Event._message(nick, chan, msg)
|
||||||
|
elif args[1] == 'QUIT':
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
Event._quit(nick)
|
||||||
|
|
||||||
|
class Loop:
|
||||||
|
def _loops():
|
||||||
|
threading.Thread(target=Loop._queue).start()
|
||||||
|
|
||||||
|
def _queue():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
if Bot._queue:
|
||||||
|
Command._raw(Bot._queue.pop(0))
|
||||||
|
except Exception as ex:
|
||||||
|
error('Error occured in the queue handler!', ex)
|
||||||
|
finally:
|
||||||
|
time.sleep(_settings['throttle'])
|
||||||
|
|
||||||
|
# Main
|
||||||
|
if _connection['proxy']:
|
||||||
|
try:
|
||||||
|
import socks
|
||||||
|
except ImportError:
|
||||||
|
error_exit('Missing PySocks module! (https://pypi.python.org/pypi/PySocks)')
|
||||||
|
if _connection['ssl']:
|
||||||
|
import ssl
|
||||||
|
else:
|
||||||
|
del _cert, _connection['verify']
|
||||||
|
Bot = IRC()
|
||||||
|
Bot._run()
|
40
skeleton/core/config.py
Normal file
40
skeleton/core/config.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# config.py
|
||||||
|
|
||||||
|
class connection:
|
||||||
|
server = 'irc.server.com'
|
||||||
|
port = 6667
|
||||||
|
proxy = None
|
||||||
|
ipv6 = False
|
||||||
|
ssl = False
|
||||||
|
ssl_verify = False
|
||||||
|
vhost = None
|
||||||
|
channel = '#dev'
|
||||||
|
key = None
|
||||||
|
|
||||||
|
class cert:
|
||||||
|
key = None
|
||||||
|
file = None
|
||||||
|
password = None
|
||||||
|
|
||||||
|
class ident:
|
||||||
|
nickname = 'skeleton'
|
||||||
|
username = 'skeleton'
|
||||||
|
realname = 'acid.vegas/skeleton'
|
||||||
|
|
||||||
|
class login:
|
||||||
|
network = None
|
||||||
|
nickserv = None
|
||||||
|
operator = None
|
||||||
|
|
||||||
|
class throttle:
|
||||||
|
command = 3
|
||||||
|
reconnect = 10
|
||||||
|
rejoin = 3
|
||||||
|
|
||||||
|
class settings:
|
||||||
|
admin = 'nick!user@host.name' # Must be in nick!user@host format (Can use wildcards here)
|
||||||
|
cmd_char = '!'
|
||||||
|
log = False
|
||||||
|
modes = None
|
229
skeleton/core/constants.py
Normal file
229
skeleton/core/constants.py
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# constants.py
|
||||||
|
|
||||||
|
# Control Characters
|
||||||
|
bold = '\x02'
|
||||||
|
color = '\x03'
|
||||||
|
italic = '\x1D'
|
||||||
|
underline = '\x1F'
|
||||||
|
reverse = '\x16'
|
||||||
|
reset = '\x0f'
|
||||||
|
|
||||||
|
# Color Codes
|
||||||
|
white = '00'
|
||||||
|
black = '01'
|
||||||
|
blue = '02'
|
||||||
|
green = '03'
|
||||||
|
red = '04'
|
||||||
|
brown = '05'
|
||||||
|
purple = '06'
|
||||||
|
orange = '07'
|
||||||
|
yellow = '08'
|
||||||
|
light_green = '09'
|
||||||
|
cyan = '10'
|
||||||
|
light_cyan = '11'
|
||||||
|
light_blue = '12'
|
||||||
|
pink = '13'
|
||||||
|
grey = '14'
|
||||||
|
light_grey = '15'
|
||||||
|
|
||||||
|
# Events
|
||||||
|
PASS = 'PASS'
|
||||||
|
NICK = 'NICK'
|
||||||
|
USER = 'USER'
|
||||||
|
OPER = 'OPER'
|
||||||
|
MODE = 'MODE'
|
||||||
|
SERVICE = 'SERVICE'
|
||||||
|
QUIT = 'QUIT'
|
||||||
|
SQUIT = 'SQUIT'
|
||||||
|
JOIN = 'JOIN'
|
||||||
|
PART = 'PART'
|
||||||
|
TOPIC = 'TOPIC'
|
||||||
|
NAMES = 'NAMES'
|
||||||
|
LIST = 'LIST'
|
||||||
|
INVITE = 'INVITE'
|
||||||
|
KICK = 'KICK'
|
||||||
|
PRIVMSG = 'PRIVMSG'
|
||||||
|
NOTICE = 'NOTICE'
|
||||||
|
MOTD = 'MOTD'
|
||||||
|
LUSERS = 'LUSERS'
|
||||||
|
VERSION = 'VERSION'
|
||||||
|
STATS = 'STATS'
|
||||||
|
LINKS = 'LINKS'
|
||||||
|
TIME = 'TIME'
|
||||||
|
CONNECT = 'CONNECT'
|
||||||
|
TRACE = 'TRACE'
|
||||||
|
ADMIN = 'ADMIN'
|
||||||
|
INFO = 'INFO'
|
||||||
|
SERVLIST = 'SERVLIST'
|
||||||
|
SQUERY = 'SQUERY'
|
||||||
|
WHO = 'WHO'
|
||||||
|
WHOIS = 'WHOIS'
|
||||||
|
WHOWAS = 'WHOWAS'
|
||||||
|
KILL = 'KILL'
|
||||||
|
PING = 'PING'
|
||||||
|
PONG = 'PONG'
|
||||||
|
ERROR = 'ERROR'
|
||||||
|
AWAY = 'AWAY'
|
||||||
|
REHASH = 'REHASH'
|
||||||
|
DIE = 'DIE'
|
||||||
|
RESTART = 'RESTART'
|
||||||
|
SUMMON = 'SUMMON'
|
||||||
|
USERS = 'USERS'
|
||||||
|
WALLOPS = 'WALLOPS'
|
||||||
|
USERHOST = 'USERHOST'
|
||||||
|
ISON = 'ISON'
|
||||||
|
|
||||||
|
# Event Numerics
|
||||||
|
RPL_WELCOME = '001'
|
||||||
|
RPL_YOURHOST = '002'
|
||||||
|
RPL_CREATED = '003'
|
||||||
|
RPL_MYINFO = '004'
|
||||||
|
RPL_ISUPPORT = '005'
|
||||||
|
RPL_TRACELINK = '200'
|
||||||
|
RPL_TRACECONNECTING = '201'
|
||||||
|
RPL_TRACEHANDSHAKE = '202'
|
||||||
|
RPL_TRACEUNKNOWN = '203'
|
||||||
|
RPL_TRACEOPERATOR = '204'
|
||||||
|
RPL_TRACEUSER = '205'
|
||||||
|
RPL_TRACESERVER = '206'
|
||||||
|
RPL_TRACESERVICE = '207'
|
||||||
|
RPL_TRACENEWTYPE = '208'
|
||||||
|
RPL_TRACECLASS = '209'
|
||||||
|
RPL_STATSLINKINFO = '211'
|
||||||
|
RPL_STATSCOMMANDS = '212'
|
||||||
|
RPL_STATSCLINE = '213'
|
||||||
|
RPL_STATSILINE = '215'
|
||||||
|
RPL_STATSKLINE = '216'
|
||||||
|
RPL_STATSYLINE = '218'
|
||||||
|
RPL_ENDOFSTATS = '219'
|
||||||
|
RPL_UMODEIS = '221'
|
||||||
|
RPL_SERVLIST = '234'
|
||||||
|
RPL_SERVLISTEND = '235'
|
||||||
|
RPL_STATSLLINE = '241'
|
||||||
|
RPL_STATSUPTIME = '242'
|
||||||
|
RPL_STATSOLINE = '243'
|
||||||
|
RPL_STATSHLINE = '244'
|
||||||
|
RPL_LUSERCLIENT = '251'
|
||||||
|
RPL_LUSEROP = '252'
|
||||||
|
RPL_LUSERUNKNOWN = '253'
|
||||||
|
RPL_LUSERCHANNELS = '254'
|
||||||
|
RPL_LUSERME = '255'
|
||||||
|
RPL_ADMINME = '256'
|
||||||
|
RPL_ADMINLOC1 = '257'
|
||||||
|
RPL_ADMINLOC2 = '258'
|
||||||
|
RPL_ADMINEMAIL = '259'
|
||||||
|
RPL_TRACELOG = '261'
|
||||||
|
RPL_TRYAGAIN = '263'
|
||||||
|
RPL_NONE = '300'
|
||||||
|
RPL_AWAY = '301'
|
||||||
|
RPL_USERHOST = '302'
|
||||||
|
RPL_ISON = '303'
|
||||||
|
RPL_UNAWAY = '305'
|
||||||
|
RPL_NOWAWAY = '306'
|
||||||
|
RPL_WHOISUSER = '311'
|
||||||
|
RPL_WHOISSERVER = '312'
|
||||||
|
RPL_WHOISOPERATOR = '313'
|
||||||
|
RPL_WHOWASUSER = '314'
|
||||||
|
RPL_ENDOFWHO = '315'
|
||||||
|
RPL_WHOISIDLE = '317'
|
||||||
|
RPL_ENDOFWHOIS = '318'
|
||||||
|
RPL_WHOISCHANNELS = '319'
|
||||||
|
RPL_LIST = '322'
|
||||||
|
RPL_LISTEND = '323'
|
||||||
|
RPL_CHANNELMODEIS = '324'
|
||||||
|
RPL_NOTOPIC = '331'
|
||||||
|
RPL_TOPIC = '332'
|
||||||
|
RPL_INVITING = '341'
|
||||||
|
RPL_INVITELIST = '346'
|
||||||
|
RPL_ENDOFINVITELIST = '347'
|
||||||
|
RPL_EXCEPTLIST = '348'
|
||||||
|
RPL_ENDOFEXCEPTLIST = '349'
|
||||||
|
RPL_VERSION = '351'
|
||||||
|
RPL_WHOREPLY = '352'
|
||||||
|
RPL_NAMREPLY = '353'
|
||||||
|
RPL_LINKS = '364'
|
||||||
|
RPL_ENDOFLINKS = '365'
|
||||||
|
RPL_ENDOFNAMES = '366'
|
||||||
|
RPL_BANLIST = '367'
|
||||||
|
RPL_ENDOFBANLIST = '368'
|
||||||
|
RPL_ENDOFWHOWAS = '369'
|
||||||
|
RPL_INFO = '371'
|
||||||
|
RPL_MOTD = '372'
|
||||||
|
RPL_ENDOFINFO = '374'
|
||||||
|
RPL_MOTDSTART = '375'
|
||||||
|
RPL_ENDOFMOTD = '376'
|
||||||
|
RPL_YOUREOPER = '381'
|
||||||
|
RPL_REHASHING = '382'
|
||||||
|
RPL_YOURESERVICE = '383'
|
||||||
|
RPL_TIME = '391'
|
||||||
|
RPL_USERSSTART = '392'
|
||||||
|
RPL_USERS = '393'
|
||||||
|
RPL_ENDOFUSERS = '394'
|
||||||
|
RPL_NOUSERS = '395'
|
||||||
|
ERR_NOSUCHNICK = '401'
|
||||||
|
ERR_NOSUCHSERVER = '402'
|
||||||
|
ERR_NOSUCHCHANNEL = '403'
|
||||||
|
ERR_CANNOTSENDTOCHAN = '404'
|
||||||
|
ERR_TOOMANYCHANNELS = '405'
|
||||||
|
ERR_WASNOSUCHNICK = '406'
|
||||||
|
ERR_TOOMANYTARGETS = '407'
|
||||||
|
ERR_NOSUCHSERVICE = '408'
|
||||||
|
ERR_NOORIGIN = '409'
|
||||||
|
ERR_NORECIPIENT = '411'
|
||||||
|
ERR_NOTEXTTOSEND = '412'
|
||||||
|
ERR_NOTOPLEVEL = '413'
|
||||||
|
ERR_WILDTOPLEVEL = '414'
|
||||||
|
ERR_BADMASK = '415'
|
||||||
|
ERR_UNKNOWNCOMMAND = '421'
|
||||||
|
ERR_NOMOTD = '422'
|
||||||
|
ERR_NOADMININFO = '423'
|
||||||
|
ERR_FILEERROR = '424'
|
||||||
|
ERR_NONICKNAMEGIVEN = '431'
|
||||||
|
ERR_ERRONEUSNICKNAME = '432'
|
||||||
|
ERR_NICKNAMEINUSE = '433'
|
||||||
|
ERR_NICKCOLLISION = '436'
|
||||||
|
ERR_USERNOTINCHANNEL = '441'
|
||||||
|
ERR_NOTONCHANNEL = '442'
|
||||||
|
ERR_USERONCHANNEL = '443'
|
||||||
|
ERR_NOLOGIN = '444'
|
||||||
|
ERR_SUMMONDISABLED = '445'
|
||||||
|
ERR_USERSDISABLED = '446'
|
||||||
|
ERR_NOTREGISTERED = '451'
|
||||||
|
ERR_NEEDMOREPARAMS = '461'
|
||||||
|
ERR_ALREADYREGISTRED = '462'
|
||||||
|
ERR_NOPERMFORHOST = '463'
|
||||||
|
ERR_PASSWDMISMATCH = '464'
|
||||||
|
ERR_YOUREBANNEDCREEP = '465'
|
||||||
|
ERR_KEYSET = '467'
|
||||||
|
ERR_CHANNELISFULL = '471'
|
||||||
|
ERR_UNKNOWNMODE = '472'
|
||||||
|
ERR_INVITEONLYCHAN = '473'
|
||||||
|
ERR_BANNEDFROMCHAN = '474'
|
||||||
|
ERR_BADCHANNELKEY = '475'
|
||||||
|
ERR_BADCHANMASK = '476'
|
||||||
|
ERR_BANLISTFULL = '478'
|
||||||
|
ERR_NOPRIVILEGES = '481'
|
||||||
|
ERR_CHANOPRIVSNEEDED = '482'
|
||||||
|
ERR_CANTKILLSERVER = '483'
|
||||||
|
ERR_UNIQOPRIVSNEEDED = '485'
|
||||||
|
ERR_NOOPERHOST = '491'
|
||||||
|
ERR_UMODEUNKNOWNFLAG = '501'
|
||||||
|
ERR_USERSDONTMATCH = '502'
|
||||||
|
RPL_STARTTLS = '670'
|
||||||
|
ERR_STARTTLS = '691'
|
||||||
|
RPL_MONONLINE = '730'
|
||||||
|
RPL_MONOFFLINE = '731'
|
||||||
|
RPL_MONLIST = '732'
|
||||||
|
RPL_ENDOFMONLIST = '733'
|
||||||
|
ERR_MONLISTFULL = '734'
|
||||||
|
RPL_LOGGEDIN = '900'
|
||||||
|
RPL_LOGGEDOUT = '901'
|
||||||
|
ERR_NICKLOCKED = '902'
|
||||||
|
RPL_SASLSUCCESS = '903'
|
||||||
|
ERR_SASLFAIL = '904'
|
||||||
|
ERR_SASLTOOLONG = '905'
|
||||||
|
ERR_SASLABORTED = '906'
|
||||||
|
ERR_SASLALREADY = '907'
|
||||||
|
RPL_SASLMECHS = '908'
|
35
skeleton/core/database.py
Normal file
35
skeleton/core/database.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# database.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
# Globals
|
||||||
|
db = sqlite3.connect(os.path.join('data', 'bot.db'), check_same_thread=False)
|
||||||
|
sql = db.cursor()
|
||||||
|
|
||||||
|
def check():
|
||||||
|
tables = sql.execute('SELECT name FROM sqlite_master WHERE type=\'table\'').fetchall()
|
||||||
|
if not len(tables):
|
||||||
|
sql.execute('CREATE TABLE IGNORE (IDENT TEXT NOT NULL);')
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
class Ignore:
|
||||||
|
def add(ident):
|
||||||
|
sql.execute('INSERT INTO IGNORE (IDENT) VALUES (?)', (ident,))
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
def check(ident):
|
||||||
|
for ignored_ident in Ignore.read():
|
||||||
|
if re.compile(ignored_ident.replace('*','.*')).search(ident):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def read():
|
||||||
|
return list(item[0] for item in sql.execute('SELECT IDENT FROM IGNORE ORDER BY IDENT ASC').fetchall())
|
||||||
|
|
||||||
|
def remove(ident):
|
||||||
|
sql.execute('DELETE FROM IGNORE WHERE IDENT=?', (ident,))
|
||||||
|
db.commit()
|
81
skeleton/core/debug.py
Normal file
81
skeleton/core/debug.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# debug.py
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
def check_libs():
|
||||||
|
if config.connection.proxy:
|
||||||
|
try:
|
||||||
|
import socks
|
||||||
|
except ImportError:
|
||||||
|
error_exit('Missing PySocks module! (https://pypi.python.org/pypi/PySocks)')
|
||||||
|
|
||||||
|
def check_privileges():
|
||||||
|
if check_windows():
|
||||||
|
if ctypes.windll.shell32.IsUserAnAdmin() != 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if os.getuid() == 0 or os.geteuid() == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_version(major):
|
||||||
|
if sys.version_info.major == major:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_windows():
|
||||||
|
if os.name == 'nt':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def clear():
|
||||||
|
if check_windows():
|
||||||
|
os.system('cls')
|
||||||
|
else:
|
||||||
|
os.system('clear')
|
||||||
|
|
||||||
|
def error(msg, reason=None):
|
||||||
|
if reason:
|
||||||
|
logging.debug(f'[!] - {msg} ({reason})')
|
||||||
|
else:
|
||||||
|
logging.debug('[!] - ' + msg)
|
||||||
|
|
||||||
|
def error_exit(msg):
|
||||||
|
raise SystemExit('[!] - ' + msg)
|
||||||
|
|
||||||
|
def info():
|
||||||
|
clear()
|
||||||
|
logging.debug('#'*56)
|
||||||
|
logging.debug('#{0}#'.format(''.center(54)))
|
||||||
|
logging.debug('#{0}#'.format('IRC Bot Skeleton'.center(54)))
|
||||||
|
logging.debug('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
|
||||||
|
logging.debug('#{0}#'.format('https://git.acid.vegas/skeleton'.center(54)))
|
||||||
|
logging.debug('#{0}#'.format(''.center(54)))
|
||||||
|
logging.debug('#'*56)
|
||||||
|
|
||||||
|
def irc(msg):
|
||||||
|
logging.debug('[~] - ' + msg)
|
||||||
|
|
||||||
|
def setup_logger():
|
||||||
|
stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
if config.settings.log:
|
||||||
|
log_file = os.path.join(os.path.join('data','logs'), 'bot.log')
|
||||||
|
file_handler = RotatingFileHandler(log_file, maxBytes=256000, backupCount=3)
|
||||||
|
logging.basicConfig(level=logging.NOTSET, format='%(asctime)s | %(message)s', datefmt='%I:%M:%S', handlers=(file_handler,stream_handler))
|
||||||
|
else:
|
||||||
|
logging.basicConfig(level=logging.NOTSET, format='%(asctime)s | %(message)s', datefmt='%I:%M:%S', handlers=(stream_handler,))
|
10
skeleton/core/functions.py
Normal file
10
skeleton/core/functions.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# functions.py
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
def is_admin(ident):
|
||||||
|
return re.compile(config.settings.admin.replace('*','.*')).search(ident)
|
283
skeleton/core/irc.py
Normal file
283
skeleton/core/irc.py
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# irc.py
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
|
||||||
|
import config
|
||||||
|
import constants
|
||||||
|
import database
|
||||||
|
import debug
|
||||||
|
import functions
|
||||||
|
|
||||||
|
# Load optional modules
|
||||||
|
if config.connection.ssl:
|
||||||
|
import ssl
|
||||||
|
if config.connection.proxy:
|
||||||
|
try:
|
||||||
|
import sock
|
||||||
|
except ImportError:
|
||||||
|
debug.error_exit('Missing PySocks module! (https://pypi.python.org/pypi/PySocks)') # Required for proxy support.
|
||||||
|
|
||||||
|
def color(msg, foreground, background=None):
|
||||||
|
if background:
|
||||||
|
return f'\x03{foreground},{background}{msg}{constants.reset}'
|
||||||
|
else:
|
||||||
|
return f'\x03{foreground}{msg}{constants.reset}'
|
||||||
|
|
||||||
|
class IRC(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.last = 0
|
||||||
|
self.slow = False
|
||||||
|
self.sock = None
|
||||||
|
self.status = True
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
try:
|
||||||
|
self.create_socket()
|
||||||
|
self.sock.connect((config.connection.server, config.connection.port))
|
||||||
|
self.register()
|
||||||
|
except socket.error as ex:
|
||||||
|
debug.error('Failed to connect to IRC server.', ex)
|
||||||
|
Events.disconnect()
|
||||||
|
else:
|
||||||
|
self.listen()
|
||||||
|
|
||||||
|
def create_socket(self):
|
||||||
|
family = socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET
|
||||||
|
if config.connection.proxy:
|
||||||
|
proxy_server, proxy_port = config.connection.proxy.split(':')
|
||||||
|
self.sock = socks.socksocket(family, socket.SOCK_STREAM)
|
||||||
|
self.sock.setblocking(0)
|
||||||
|
self.sock.settimeout(15)
|
||||||
|
self.sock.setproxy(socks.PROXY_TYPE_SOCKS5, proxy_server, int(proxy_port))
|
||||||
|
else:
|
||||||
|
self.sock = socket.socket(family, socket.SOCK_STREAM)
|
||||||
|
if config.connection.vhost:
|
||||||
|
self.sock.bind((config.connection.vhost, 0))
|
||||||
|
if config.connection.ssl:
|
||||||
|
ctx = ssl.SSLContext()
|
||||||
|
if config.cert.file:
|
||||||
|
ctx.load_cert_chain(config.cert.file, config.cert.key, config.cert.password)
|
||||||
|
if config.connection.ssl_verify:
|
||||||
|
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||||
|
ctx.load_default_certs()
|
||||||
|
else:
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
self.sock = ctx.wrap_socket(self.sock)
|
||||||
|
|
||||||
|
def listen(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
data = self.sock.recv(2048).decode('utf-8')
|
||||||
|
for line in (line for line in data.split('\r\n') if line):
|
||||||
|
debug.irc(line)
|
||||||
|
if len(line.split()) >= 2:
|
||||||
|
Events.handle(line)
|
||||||
|
except (UnicodeDecodeError,UnicodeEncodeError):
|
||||||
|
pass
|
||||||
|
except Exception as ex:
|
||||||
|
debug.error('Unexpected error occured.', ex)
|
||||||
|
break
|
||||||
|
Events.disconnect()
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
if config.login.network:
|
||||||
|
Commands.raw('PASS ' + config.login.network)
|
||||||
|
Commands.raw(f'USER {config.ident.username} 0 * :{config.ident.realname}')
|
||||||
|
Commands.nick(config.ident.nickname)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Commands:
|
||||||
|
def action(chan, msg):
|
||||||
|
Commands.sendmsg(chan, f'\x01ACTION {msg}\x01')
|
||||||
|
|
||||||
|
def ctcp(target, data):
|
||||||
|
Commands.sendmsg(target, f'\001{data}\001')
|
||||||
|
|
||||||
|
def error(target, data, reason=None):
|
||||||
|
if reason:
|
||||||
|
Commands.sendmsg(target, '[{0}] {1} {2}'.format(color('!', constants.red), data, color('({0})'.format(reason), constants.grey)))
|
||||||
|
else:
|
||||||
|
Commands.sendmsg(target, '[{0}] {1}'.format(color('!', constants.red), data))
|
||||||
|
|
||||||
|
def identify(nick, password):
|
||||||
|
Commands.sendmsg('nickserv', f'identify {nick} {password}')
|
||||||
|
|
||||||
|
def invite(nick, chan):
|
||||||
|
Commands.raw(f'INVITE {nick} {chan}')
|
||||||
|
|
||||||
|
def join_channel(chan, key=None):
|
||||||
|
Commands.raw(f'JOIN {chan} {key}') if msg else Commands.raw('JOIN ' + chan)
|
||||||
|
|
||||||
|
def mode(target, mode):
|
||||||
|
Commands.raw(f'MODE {target} {mode}')
|
||||||
|
|
||||||
|
def nick(nick):
|
||||||
|
Commands.raw('NICK ' + nick)
|
||||||
|
|
||||||
|
def notice(target, msg):
|
||||||
|
Commands.raw(f'NOTICE {target} :{msg}')
|
||||||
|
|
||||||
|
def oper(user, password):
|
||||||
|
Commands.raw(f'OPER {user} {password}')
|
||||||
|
|
||||||
|
def part(chan, msg=None):
|
||||||
|
Commands.raw(f'PART {chan} {msg}') if msg else Commands.raw('PART ' + chan)
|
||||||
|
|
||||||
|
def quit(msg=None):
|
||||||
|
Commands.raw('QUIT :' + msg) if msg else Commands.raw('QUIT')
|
||||||
|
|
||||||
|
def raw(msg):
|
||||||
|
Bot.sock.send(bytes(msg + '\r\n', 'utf-8'))
|
||||||
|
|
||||||
|
def sendmsg(target, msg):
|
||||||
|
Commands.raw(f'PRIVMSG {target} :{msg}')
|
||||||
|
|
||||||
|
def topic(chan, text):
|
||||||
|
Commands.raw(f'TOPIC {chan} :{text}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Events:
|
||||||
|
def connect():
|
||||||
|
if config.settings.modes:
|
||||||
|
Commands.mode(config.ident.nickname, '+' + config.settings.modes)
|
||||||
|
if config.login.nickserv:
|
||||||
|
Commands.identify(config.ident.nickname, config.login.nickserv)
|
||||||
|
if config.login.operator:
|
||||||
|
Commands.oper(config.ident.username, config.login.operator)
|
||||||
|
Commands.join_channel(config.connection.channel, config.connection.key)
|
||||||
|
|
||||||
|
def ctcp(nick, chan, msg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def disconnect():
|
||||||
|
Bot.sock.close()
|
||||||
|
time.sleep(config.throttle.reconnect)
|
||||||
|
Bot.connect()
|
||||||
|
|
||||||
|
def invite(nick, chan):
|
||||||
|
if nick == config.ident.nickname and chan == config.connection.channe:
|
||||||
|
Commands.join_channel(config.connection.channel, config.connection.key)
|
||||||
|
|
||||||
|
def join_channel(nick, chan):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def kick(nick, chan, kicked):
|
||||||
|
if kicked == config.ident.nickname and chan == config.connection.channel:
|
||||||
|
time.sleep(config.throttle.rejoin)
|
||||||
|
Commands.join_channel(chan, config.connection.key)
|
||||||
|
|
||||||
|
def message(nick, ident, chan, msg):
|
||||||
|
try:
|
||||||
|
if chan == config.connection.channel and Bot.status:
|
||||||
|
if msg.startswith(config.settings.cmd_char):
|
||||||
|
if not database.Ignore.check(ident):
|
||||||
|
if time.time() - Bot.last < config.throttle.command and not functions.is_admin(ident):
|
||||||
|
if not Bot.slow:
|
||||||
|
Commands.sendmsg(chan, color('Slow down nerd!', constants.red))
|
||||||
|
Bot.slow = True
|
||||||
|
elif Bot.status or functions.is_admin(ident):
|
||||||
|
Bot.slow = False
|
||||||
|
args = msg.split()
|
||||||
|
if len(args) == 1:
|
||||||
|
if cmd == 'test':
|
||||||
|
Commands.sendmsg(chan, 'It works!')
|
||||||
|
elif len(args) >= 2:
|
||||||
|
if cmd == 'echo':
|
||||||
|
Commands.sendmsg(chan, args)
|
||||||
|
Bot.last = time.time()
|
||||||
|
except Exception as ex:
|
||||||
|
Commands.error(chan, 'Command threw an exception.', ex)
|
||||||
|
|
||||||
|
def nick_in_use():
|
||||||
|
debug.error('The bot is already running or nick is in use.')
|
||||||
|
|
||||||
|
def part(nick, chan):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def private(nick, ident, msg):
|
||||||
|
if functions.is_admin(ident):
|
||||||
|
args = msg.split()
|
||||||
|
if msg == '.ignore':
|
||||||
|
ignores = database.Ignore.read()
|
||||||
|
if ignores:
|
||||||
|
Commands.sendmsg(nick, '[{0}]'.format(color('Ignore List', constants.purple)))
|
||||||
|
for user in ignores:
|
||||||
|
Commands.sendmsg(nick, color(user, constants.yellow))
|
||||||
|
Commands.sendmsg(nick, '{0} {1}'.format(color('Total:', constants.light_blue), color(len(ignores), constants.grey)))
|
||||||
|
else:
|
||||||
|
Commands.error(nick, 'Ignore list is empty!')
|
||||||
|
elif msg == '.off':
|
||||||
|
Bot.status = False
|
||||||
|
Commands.sendmsg(nick, color('OFF', constants.red))
|
||||||
|
elif msg == '.on':
|
||||||
|
Bot.status = True
|
||||||
|
Commands.sendmsg(nick, color('ON', constants.green))
|
||||||
|
elif len(args) == 3:
|
||||||
|
if args[0] == '.ignore':
|
||||||
|
if args[1] == 'add':
|
||||||
|
user_ident = args[2]
|
||||||
|
if user_ident not in database.Ignore.hosts():
|
||||||
|
database.Ignore.add(nickname, user_ident)
|
||||||
|
Commands.sendmsg(nick, 'Ident {0} to the ignore list.'.format(color('added', constants.green)))
|
||||||
|
else:
|
||||||
|
Commands.error(nick, 'Ident is already on the ignore list.')
|
||||||
|
elif args[1] == 'del':
|
||||||
|
user_ident = args[2]
|
||||||
|
if user_ident in database.Ignore.hosts():
|
||||||
|
database.Ignore.remove(user_ident)
|
||||||
|
Commands.sendmsg(nick, 'Ident {0} from the ignore list.'.format(color('removed', constants.red)))
|
||||||
|
else:
|
||||||
|
Commands.error(nick, 'Ident does not exist in the ignore list.')
|
||||||
|
|
||||||
|
def quit(nick):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle(data):
|
||||||
|
args = data.split()
|
||||||
|
if data.startswith('ERROR :Closing Link:'):
|
||||||
|
raise Exception('Connection has closed.')
|
||||||
|
elif args[0] == 'PING':
|
||||||
|
Commands.raw('PONG ' + args[1][1:])
|
||||||
|
elif args[1] == constants.RPL_WELCOME:
|
||||||
|
Events.connect()
|
||||||
|
elif args[1] == constants.ERR_NICKNAMEINUSE:
|
||||||
|
Events.nick_in_use()
|
||||||
|
elif args[1] == constants.INVITE and len(args) == 4:
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[3][1:]
|
||||||
|
Events.invite(nick, chan)
|
||||||
|
elif args[1] == constants.JOIN and len(args) == 3:
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2][1:]
|
||||||
|
Events.join_channel(nick, chan)
|
||||||
|
elif args[1] == constants.KICK and len(args) >= 4:
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2]
|
||||||
|
kicked = args[3]
|
||||||
|
Events.kick(nick, chan, kicked)
|
||||||
|
elif args[1] == constants.PART and len(args) >= 3:
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
chan = args[2]
|
||||||
|
Events.part(nick, chan)
|
||||||
|
elif args[1] == constants.PRIVMSG and len(args) >= 4:
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
ident = args[0].split('!')[1]
|
||||||
|
chan = args[2]
|
||||||
|
msg = data.split(f'{args[0]} PRIVMSG {chan} :')[1]
|
||||||
|
if msg.startswith('\001'):
|
||||||
|
Events.ctcp(nick, chan, msg)
|
||||||
|
elif chan == config.ident.nickname:
|
||||||
|
Events.private(nick, ident, msg)
|
||||||
|
else:
|
||||||
|
Events.message(nick, ident, chan, msg)
|
||||||
|
elif args[1] == constants.QUIT:
|
||||||
|
nick = args[0].split('!')[0][1:]
|
||||||
|
Events.quit(nick)
|
||||||
|
|
||||||
|
Bot = IRC()
|
4
skeleton/data/cert/.gitignore
vendored
Normal file
4
skeleton/data/cert/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
4
skeleton/data/logs/.gitignore
vendored
Normal file
4
skeleton/data/logs/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
4
skeleton/modules/.gitignore
vendored
Normal file
4
skeleton/modules/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
24
skeleton/skeleton.py
Normal file
24
skeleton/skeleton.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# IRC Bot Skeleton - Developed by acidvegas in Python (https://acid.vegas/skeleton)
|
||||||
|
# skeleton.py
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.dont_write_bytecode = True
|
||||||
|
os.chdir(sys.path[0] or '.')
|
||||||
|
sys.path += ('core','modules')
|
||||||
|
|
||||||
|
import debug
|
||||||
|
|
||||||
|
debug.setup_logger()
|
||||||
|
debug.info()
|
||||||
|
if not debug.check_version(3):
|
||||||
|
debug.error_exit('Python 3 is required!')
|
||||||
|
if debug.check_privileges():
|
||||||
|
debug.error_exit('Do not run as admin/root!')
|
||||||
|
debug.check_libs()
|
||||||
|
import database
|
||||||
|
database.check()
|
||||||
|
import irc
|
||||||
|
irc.Bot.connect()
|
Loading…
Reference in New Issue
Block a user