This repository has been archived by the owner on Sep 18, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIRCSession.py
124 lines (102 loc) · 3.38 KB
/
IRCSession.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from threading import Thread, Lock
import socket
import random
import time
import sys
class IRCSession(object):
def __init__(self, server, port, nickname, username, realname, password):
self.s = socket.socket()
self.s.connect((server,port))
self.rbuf = self.s.makefile("rb")
self.nickname = nickname
self.privmsgHooks = dict()
self.responseLocks = dict()
if password:
self.send("PASS {0}".format(password))
self.send("NICK {0}".format(nickname))
self.send("USER {0} 127.0.0.1 {1} :{2}".format(username, server, realname))
Thread(target=self.readloop).start()
# Wait until MOTD etc gets done
self.waitForResponse("005")
# our debug-hook - in most cases do nothing
def debug(self, msg):
print(msg)
pass
def send(self, data):
self.debug(' > ' + data)
self.s.send(data.encode('utf-8') + b'\n')
def waitForResponse(self, code, callback = None):
if code not in self.responseLocks:
self.responseLocks[code] = Lock()
self.responseLocks[code].acquire()
self.responseLocks[code].acquire()
try:
self.responseLocks[code].release()
except:
pass
if callback != None:
callback()
def readloop(self):
while True:
try:
line = self.rbuf.readline().strip().decode('utf-8')
self.debug(' < ' + line)
self.parseLine(line)
except UnicodeDecodeError:
pass
def join(self, chan, password = ''):
self.send('JOIN {0} {1}'.format(chan, password))
def leave(self, chan, reason):
self.send('PART {0} :{1}'.format(chan, reason))
def post(self, chan, msg):
for line in msg.split('\n'):
self.send('PRIVMSG {0} :{1}'.format(chan, line.rstrip()))
def registerCommand(self, prefix, function):
self.privmsgHooks[prefix] = function
def parseLine(self, line):
if line == '' or line == None:
return
# irc-syntax. ":nick!user@host restofline"
if line.startswith(':'):
source = line.split(' ', 2)[0].lstrip(':').split('!', 2)[0].lower()
line = ' '.join(line.split(' ', 2)[1:])
# nick is in use
if line.startswith('433'):
self.nickname = self.nickname + "_"
self.send('NICK ' + self.nickname)
# any numeric response we are waiting for
if line[:3] in self.responseLocks:
try:
self.responseLocks[line[:3]].release()
except:
pass
# handle commands via PRIVMSG
elif line.upper().startswith('PRIVMSG'):
# irc-syntax. "PRIVMSG <target> <message>"
(cmd, chan) = line.split(' ')[0:2]
if not chan.startswith('#'):
chan = source
body = ' '.join(line.split(' ')[2:])
# irc-clients have to send a prefixed message to be rfc-compatible
if body.startswith(':'): body = body[1:]
for command in self.privmsgHooks.keys():
if body.lower().startswith(command.lower()):
self.privmsgHooks[command](IRCContext(self, chan, source), command, body[len(command):])
# only needed if we want to auto-join every channel we get invited to
#elif line.upper().startswith('INVITE'):
# (cmd, nick) = line.split(' ')[0:2]
# chan = line.split(' ')[2]
# if chan.startswith(':'): chan = chan[1:]
# self.join(chan)
# reply PINGs (else connection will be closed by server)
elif line.upper().startswith('PING'):
self.send('PONG ' + line[5:])
class IRCContext(object):
def __init__(self, irc, channel, sender):
self.irc = irc
self.channel = channel
self.sender = sender
def reply(self, message):
self.irc.post(self.channel, self.sender + ": " + message)