This commit is contained in:
river 2021-05-24 17:48:04 +01:00
parent ba860ffc4e
commit e057eed3ac
6 changed files with 125 additions and 1 deletions

6
.gitignore vendored
View File

@ -138,3 +138,9 @@ dmypy.json
# Cython debug symbols
cython_debug/
# config.py
config.py
# ~ files
*~

View File

@ -1,3 +1,40 @@
# King-Leonidas
My IRC bot in python
My IRC bot in python
## Overview
* `king-leonidas`: The main script.
* `config.py`: The settings.
* `irc.py`: Generic code to connect to and maintain an IRC connection.
* `bot.py`: Stateless IRC bot code that processes lines.
## Usage
### Setup
```
python -m venv venv
source venv/bin/activate
```
### Launch
```
./king-leonidas
```
### Reload bot.py
```
pkill -HUP king-leonidas
```
## To do
* join after ident, or SASL
* automatically restart on crash
* handle should be split into functions that deal with channel messages, privmsgs, etc.
* bot state saved to disk?
* provide a logfile to write to
* would like to be able to trigger a reload using a Control-something signal in the bot itself, rather than pkill in a separate terminal.

26
bot.py Normal file
View File

@ -0,0 +1,26 @@
import re
print("[ + ] Loaded bot.py")
def handle(state, line, f):
print("[IRC] %s" % line)
# ping/pong
m = re.search('^PING :(.*)$', line)
if m:
print(("PONG %s" % m.group(1)), file=f)
return
# kick anyone who joins
m = re.search('^:(.+)!(.+)@(.+) JOIN (.+)$', line)
if m:
print('KICK %s %s :%s' % (m.group(4), m.group(1), "THIS! IS! SPARTA!"), file=f)
return
# increment a counter
m = re.search('^[^ ]+ PRIVMSG ([^ ]+) :\+\+$', line)
if m:
if 'ctr' not in state:
state['ctr'] = 0
print('PRIVMSG %s :%s' % (m.group(1), state['ctr']), file=f)
state['ctr'] += 1

6
config.py.example Normal file
View File

@ -0,0 +1,6 @@
class Config():
username = 'UnHelpfulBot'
password = 'Pa55W0Rd*(@#%'
server = 'irc.relay.chat'
port = '6697'
channel = '#chatters'

34
irc.py Normal file
View File

@ -0,0 +1,34 @@
import sys
import socket
import ssl
import time
import importlib
import bot
class IRC():
def __init__(self, config):
self.config = config
self.trigger_reload = False
self.state = {}
def go(self):
s0 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = ssl.wrap_socket(s0)
s.connect((self.config.server, int(self.config.port)))
f = s.makefile(mode='rw', buffering=1, encoding='utf-8', newline='\r\n')
print("USER %s %s %s %s" % (self.config.username, self.config.username, self.config.username, self.config.username), file=f)
print("NICK %s" % (self.config.username), file=f)
print("PRIVMSG NickServ :Identify %s\n" % (self.config.password), file=f)
print("JOIN %s" % (self.config.channel), file=f)
for line in f:
if self.trigger_reload:
module = importlib.import_module('bot')
importlib.reload(module)
self.trigger_reload = False
try:
bot.handle(self.state, line.rstrip(), f)
except:
print("Unexpected error:", sys.exc_info())

15
king-leonidas Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
import signal
from config import Config
from irc import IRC
bot = IRC(Config)
def sighup_handler(signo, frame):
bot.trigger_reload = True
signal.signal(signal.SIGHUP, sighup_handler)
bot.go()