forked from BlueMoon-Labs/MOLOT-BlueMoon-Station
-
Notifications
You must be signed in to change notification settings - Fork 0
/
minibot.py
162 lines (143 loc) · 4.97 KB
/
minibot.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
# This bot was made by tkdrg.
# Ask #[email protected] if this breaks.
# See LICENSE-bot_folder.txt for the license of the files in this folder.
from config import *
import collections
import time
import pickle
import socket
import sys
import threading
import logging
import logging.handlers as handlers
import signal
global irc
# Set to false when we've been killed
running = True
# times we've attempted to connect to server
con_attempts = 0
## Set up a logger object
logger = logging.getLogger('minibot')
logger.setLevel(logging.DEBUG)
# create a file handler (rolls over midnight, keeps 7 days of log
handler = handlers.TimedRotatingFileHandler('minibot.log', when='midnight', backupCount=7)
# most verbose
handler.setLevel(logging.DEBUG)
#only send errors/notifications to the terminal
iohandler = logging.StreamHandler()
iohandler.setLevel(logging.INFO)
# create a logging format
#time - name - level - message (string)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M')
handler.setFormatter(formatter)
iohandler.setFormatter(formatter)
#finally attach them to the logger object
logger.addHandler(handler)
logger.addHandler(iohandler)
def setup_irc_socket():
global irc, running, con_attempts, logger
s = socket.socket()
s.settimeout(240)
#why not reuse running here? because we want to break this loop if someone sigkills us
connected = False
while running and con_attempts < 3 and not connected:
try:
s.connect((server, port))
except socket.error:
logger.exception("Unable to connect to server {0}:{1}, attempting to reconnect in 20 seconds, Attempt number:{2}".format(server, port, con_attempts))
con_attempts += 1
time.sleep(20)
continue
logger.info("Connection established to server {0}:{1}.".format(server, port))
connected = True
if connected:
s.send(bytes("NICK {0}\r\n".format(nick), "UTF-8"))
s.send(bytes("USER {0} {1} {2} :{3}\r\n".format(ident, server, name, realname), "UTF-8"))
else:
logger.error("Unable to connect, shutting down")
running = False
return s
def setup_nudge_socket():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 45678)) # localhost:nudge_port
s.listen(5)
logger.info("Nudge socket up and listening")
return s
def nudge_handler():
global irc, running, con_attempts, logger
nudge = setup_nudge_socket()
message_queue = collections.deque()
while running:
if len(message_queue):
message = message_queue.popleft()
else:
try:
s, ip = nudge.accept()
except:
logger.exception("Nudge socket lost, attempting to reopen.")
nudge = setup_nudge_socket()
continue
rawdata = s.recv(1024)
s.close()
data = pickle.loads(rawdata)
logger.debug(data)
if data["ip"][0] == "#":
message = "{0} :AUTOMATIC ANNOUNCEMENT : {1}\r\n".format(data["ip"], str(" ".join(data["data"])))
else:
message = "{0} :AUTOMATIC ANNOUNCEMENT : {1} | {2}\r\n".format(defaultchannel, data["ip"], str(" ".join(data["data"])))
try:
irc.send(bytes("PRIVMSG {0}".format(message), "UTF-8"))
except:
logger.exception("Nudge received without IRC socket, appending to queue.")
logger.debug("Message: {0}".format(message))
message_queue.append(message)
def irc_handler():
global irc, running, con_attempts, logger
while running:
try:
buf = irc.recv(1024).decode("UTF-8").split("\n")
for i in buf:
logger.debug(i)
if i[0:4] == "PING":
irc.send(bytes("PONG {0}\r\n".format(i[5:]), "UTF-8"))
else:
l = i.split(" ")
if len(l) < 2:
continue
elif l[1] == "001":
logger.info("connected and registered, identifing and joining channels")
irc.send(bytes("PRIVMSG NickServ :IDENTIFY {0}\r\n".format(password), "UTF-8"))
time.sleep(1)
for channel in channels:
irc.send(bytes("JOIN {0}\r\n".format(channel), "UTF-8"))
elif l[1] == "477":
logger.error("Error: Nickname was not registered when joining {0}. Reauthing and retrying...".format(l[3]))
irc.send(bytes("PRIVMSG NickServ :IDENTIFY {0}\r\n".format(password), "UTF-8"))
time.sleep(5)
irc.send(bytes("JOIN {0}\r\n".format(l[3]), "UTF-8"))
elif l[1] == "433":
logger.error("Error: Nickname already in use. Attempting to use alt nickname if available, sleeping 60s otherwise...")
if(altnick):
irc.send(bytes("NICK {0}\r\n".format(altnick), "UTF-8"))
else:
time.sleep(60)
irc = setup_irc_socket()
except InterruptedError as e:
logger.exception("Interrupted, probably killed.")
continue
except:
logger.exception("Lost connection to IRC server.")
irc = setup_irc_socket()
def signal_handler(signum, frame):
global irc, running, con_attempts, logger
logger.info("Recieved term kill, closing")
running = False
if __name__ == "__main__":
#listen to signals (quit on ctrl c or kill from OS)
signal.signal(signal.SIGINT, signal_handler)
irc = setup_irc_socket()
t = threading.Thread(target=nudge_handler)
t.daemon = True
t.start()
irc_handler()