· 10 years ago · Sep 09, 2016, 03:48 PM
1# -*- coding: iso-8859-1 -*-
2
3import socket
4import botcommands
5import threading
6from threading import Timer
7import serial
8import sys
9import signal
10import sched, time
11import sqlite3
12from datetime import datetime
13from markovchain import Markov
14import settings
15
16#onkelmia: ctrl-c ei lopeta threadeja, eikä tajua reconnectata jos yhteys
17#katkeaa. lisäksi ei tajua vaihtaa nikkiä jos nikki rekisteröity
18
19HOST = "" #sallitut ip:t. kaikki sallittu
20PORT = 6969 #portti avattu palvelimelta
21
22s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
23s.bind((HOST, PORT))
24s.listen(4) #maximum number of queued connections
25clients = [] #list of clients connected
26lock = threading.Lock()
27
28DONE = 0
29
30def signal_handler(signal, frame):
31 print('signaali kaapattu, suljetaan...')
32 DONE = 1
33
34class chatServer(threading.Thread): #client
35 def __init__(self, (socket,address), bot):
36 threading.Thread.__init__(self)
37 self.socket = socket
38 self.address= address
39 self.laite = ""
40 self.bot = bot
41
42 def run(self):
43 lock.acquire()
44 clients.append(self)
45 lock.release()
46 print '%s:%s connected.' % self.address #ip:port
47 while not DONE: #pää looppi
48 data = str(self.socket.recv(1024))
49 print("%s:%s says: \"%s\"" % (self.address[0], self.address[1], data))#ip:port says: "data"
50 if not data:
51 break
52 if self.laite == "":
53 if data[:5] == "kahvi":
54 self.laite = "kahvi"
55 continue
56 if self.laite == "kahvi":
57 if data[:4] == "sano":
58 self.bot.sendmsg("Kahvi: %s" % data[5:])
59 #this.socket.send(bytes("ok"))
60 continue
61 self.socket.close()
62 print '%s:%s disconnected.' % self.address
63 lock.acquire()
64 clients.remove(self)
65 lock.release()
66
67class newSocket:
68 def setBot(self, bot):
69 self.bot = bot
70
71 def run(self):
72 while 1: #uusi kierros aina uuden yhteyden muodostuessa
73 chatServer(s.accept(), self.bot).start()
74
75
76def sanoKahville(viesti):
77 kahviYhdistetty = 0
78 for c in clients: #lähetetään kaikille yhdistetyille kahvi clienteille
79 if c.laite == "kahvi":
80 kahviyhdistetty = 1
81 c.socket.send(bytes(viesti))
82 if kahviyhdistetty == 0:
83 #yhtään kahvi clienttiä ei yhdistetty
84 #todo: asian voisi ilmoittaa irkkiin?
85 pass
86
87def suljeClientit():
88 for c in clients:
89 c.socket.close()
90
91
92# Onhan se aika perverssi
93def perverse_format_datetime(orig):
94 dt = datetime.strptime(orig, "%Y-%m-%d %H:%M");
95 return dt.strftime("%M-%H-%d-%m-%y");
96
97#hoitaa AVR:n kanssa kommunikoinnin
98
99class SerialReader:
100
101 def __init__(self):
102 self.database = sqlite3.connect("rawdata.sqlite")
103 self.connected = False
104 self.portInUse = False
105 self.done = False
106 port = '/dev/ttyS0'
107 baud = 9600
108
109 self.database.execute(
110 '''CREATE TABLE IF NOT EXISTS rawdata
111 (aika TEXT, -- Vaatii tietyn formaatin
112 lampo INTEGER, -- oispa kaljaa
113 ovi INTEGER,
114 valo INTEGER);''')
115 self.database.commit()
116 self.database.close()
117
118 self.serial_port = serial.Serial(port, baud, parity=serial.PARITY_EVEN, timeout=None)
119
120 self.tmr = Timer(60, self.readData, ())
121 self.tmr.start()
122
123 #lukee datat 10min välein AVR:ltä
124 def readData(self):
125 print "reading"
126 self.portInUse = True
127 self.serial_port.write("A".encode())
128 self.serial_port.write(datetime.now().strftime('%S%M%H%d%m%y\r\n'))
129 self.serial_port.write("D".encode())
130
131 self.tmr = Timer(600, self.readData, ())
132 self.tmr.start()
133
134 def setBot(self, bot):
135 self.bot = bot
136
137 #katsotaan mitä sarjaportista tuli ja toimitaan sen mukaan
138 def handle_data(self, data):
139 self.database = sqlite3.connect("rawdata.sqlite")
140 data = data.strip('\n')
141 data = data.strip('\r')
142 #jostain syystä linuxilla sarjaportista luettuun dataan
143 #tuli rivin alkuun jotain kakkaa, poistetaan se
144 if ord(data[0]) > 128:
145 data = data[1:]
146 print data
147 if data.startswith("auki"):
148 self.bot.sendmsg("Ovi on auki.")
149 elif data.startswith("kiinni"):
150 self.bot.sendmsg("Ovi on kiinni.")
151 elif data.startswith("ovi sulkeutui"):
152 self.bot.sendnotice("Ovi sulkeutui.")
153 elif data.startswith("ovi aukesi"):
154 self.bot.sendnotice("Ovi aukesi.")
155 elif data.startswith("valo pois"):
156 self.bot.sendmsg("Valot ovat pois päältä.")
157 elif data.startswith("valo"):
158 self.bot.sendmsg("Valot ovat päällä.")
159 elif data.isdigit() and len(data) < 5:
160 self.bot.sendmsg("Kerhon lämpötila %s astetta." % data)
161 elif data.startswith("REC"):
162 c = self.database.cursor()
163 data = data.split(',')
164 if len(data) < 9:
165 self.serial_port.write("E".encode())
166 return
167 try:
168 c.execute('''INSERT INTO rawdata VALUES
169 (?,?,?,?);
170 ''', # konvertoidaan se oikeeks päivämääräks kantaan
171 ('20' + data[5] + '-' + data[4] + '-' + data[3] + ' ' + data[2] + ':' + data[1],
172 data[6],
173 data[7],
174 data[8]));
175 self.database.commit()
176 print "data inserted"
177 except sqlite3.Error as e:
178 print e.message
179 self.serial_port.write('K'.encode())
180 #jos datan vastaanotto loppui, luodaan uusi csv webbikikkareelle
181 elif data.startswith("empty"):
182 self.portInUse = False
183 data = []
184
185
186 else:
187 print "*prööt*"
188
189 self.database.close()
190
191 def read_from_port(self):
192 print "reading\n"
193 while not (self.connected or DONE):
194 self.connected = True
195
196 while True:
197 reading = self.serial_port.readline()
198 self.handle_data(reading)
199 def write(self, string):
200 if not self.portInUse:
201 self.serial_port.write(string)
202
203 def stopTimer(self):
204 self.tmr.cancel()
205
206class Ircbot:
207
208 def __init__( self, serial):
209
210 self.ser = serial
211
212 #self.markov = markov
213
214 # välttämättömiä tietoja
215
216 self.server = settings.server
217 self.port = settings.port
218 self.username = settings.name
219 self.realname = settings.name
220 self.nick = settings.nick
221 self.msgcount = 0
222 self.privmsgcount = 0
223 self.minutetimer = 0
224
225
226 self.connected = False
227 self.reconnectioninterval = timedelta(0, 0, 0, 0, 5) # 5 minutes
228 self.lastconnection = datetime(2015, 1, 1) # Arbitrary date in the past
229
230 # luodaan socket
231
232 self.socket = None
233
234 # haetaan botille komennot
235
236 self.commands = botcommands.command_dict
237
238 # päälooppia toistettan kunnes done = 1. asetetaan
239
240 self.done = 0
241
242 # kanava jolle botti halutaan
243
244 self.channel = settings.channel
245
246 self.tmr = Timer(60, self.clearCounter, ())
247 self.tmr.start()
248
249 #nollataan spämminestolaskuri
250 def clearCounter(self):
251 self.minutetimer += 1
252 if self.minutetimer % 5 == 0 and not self.connected:
253 self.connect()
254 if self.minutetimer % 10 == 0:
255 if self.connected and self.nick != settings.nick:
256 self.send('NICK %s' % settings.nick)
257 if (self.minutetimer >= 720):
258 self.minutetimer = 0
259 self.msgcount = 0
260 self.privmsgcount = 0
261 self.tmr = Timer(60, self.clearCounter, ())
262 self.tmr.start()
263
264 def send( self, string ):
265 if not self.socket is None:
266 try:
267 self.socket.send( (string + '\r\n'))
268 except:
269 print "Unable to send " + string
270
271 #lähettää viestin, jos lähetetty 6 viestiä minuutissa, ei tee mitään
272 def sendmsg( self, string):
273 if self.msgcount < 6:
274 self.msgcount += 1
275 self.send(('PRIVMSG %s :' % self.channel) + string)
276
277 def sendprivmsg(self, target, string):
278 if self.privmsgcount < 10:
279 self.privmsgcount += 1
280 self.send(('PRIVMSG %s :' % target) + string)
281
282 def sendnotice( self, string):
283 if self.msgcount < 6:
284 self.msgcount += 1
285 self.send(('NOTICE %s :' % self.channel) + string)
286
287 def connect( self ):
288 if (self.lastconnection + self.reconnectioninterval) < datetime.now():
289 try:
290 self.lastconnection = datetime.now()
291 self.socket = socket.socket()
292 self.socket.connect( ( self.server, self.port ) )
293 self.send( 'NICK %s' % self.nick )
294 self.send( 'USER %s a a :%s' % ( self.username, self.realname ) )
295 self.send( 'JOIN %s' % self.channel )
296 except socket.error:
297 print "Error connecting to " + self.server + ":" + str(self.port) + "!"
298
299 def check( self, line ):
300
301 print line
302 line = line.split(' ')
303
304 # vastataan pingiin muuten serveri katkaisee yhteyden
305 if line[0] == 'PING':
306
307 self.send( 'PONG ' + line[1] ) #PING :nönnönnöö kuuluu vastata PONG :nönnönnöö. ircnetissä ei tosin ole merkitystä
308
309 elif len(line) > 1:
310 if (line[1] == '437' or line[1] == '433'): #437=nick collision, 433=nickname in use
311 if self.connected == False:
312 if self.nick == settings.nick2:
313 self.send( 'QUIT')
314 self.nick = settings.nick
315 print "Unable to use " + settings.nick + " or " + settings.nick2 + ", trying again later"
316 else:
317 self.nick = settings.nick2
318 self.send( 'NICK %s' % self.nick )
319 self.send( 'JOIN %s' % self.channel )
320 elif (line[1] == '366'): # we've joined a channel
321 self.connected = True
322 elif line[1] == 'NICK':
323 self.nick = line[2][1:]
324 else:
325 try:
326
327 if line[2] == self.nick:
328 target = line[0][1:line[0].find('!')]
329 self.commands[ line[3] ].main( self , line, target)
330 return
331
332 #if line[3][1] != '!':
333 # self.markov.learn(line[3:])
334
335 # suoritetaan komennot jos niitä on tullut
336 if line[3] == ":!kahvi": #kahvibottia varten tulleet komennot
337 if line[4] == "lisaa":
338 sanoKahville("lisaa_aikaa")
339 return
340 if line[4] == "jaljella":
341 sanoKahville("kerro_jaljella")
342 return
343 if line[4] == "yhteensa":
344 sanoKahville("kerro_yhteensa")
345 return
346 if line[4] == "lampo":
347 sanoKahville("kerro_lampotila")
348 return
349 if line[4] == "viesti":
350 line2 = ""
351 for i in line[5:]:
352 line2 += i + " " #palautetaan välilyönneistä poikkaistut viestit yhdeksi pötköksi
353 sanoKahville("viesti " + line2)
354 return
355 else:
356 return
357 self.commands[ line[3] ].main( self , line ) #botcommands.py
358 except:
359 pass
360
361 def generate_markov(self, word1=None, word2=None):
362 if self.markov is not None:
363 textmessage = ""
364 if word2 is not None:
365 textmessage = self.markov.generate_starting_phrase(word1, word2)
366 elif word1 is not None:
367 textmessage = self.markov.generate_min_words_starting_with(word1)
368 else:
369 textmessage = self.markov.generate_min_words(8)
370 print textmessage
371 self.sendmsg(textmessage)
372 else:
373 print "No markov module initialized!\n"
374
375 def mainloop( self ):
376
377 buffer = ''
378
379 while not (self.done or DONE):
380
381 try:
382 if not self.socket is None:
383 newline = self.socket.recv( 4096 )
384 if len(newline) == 0:
385 if self.connected == True:
386 print "Disconnected from server!"
387 self.connected = False
388 else:
389 buffer += newline
390 buffer = buffer.split( '\r\n' )
391 for line in buffer[0:-1]:
392 self.check( line )
393 buffer = buffer[-1]
394 except socket.error:
395 if self.connected == True:
396 print "Disconnected from server!"
397 self.connected = False
398
399 print "Suljetaan botti..."
400 self.tmr.cancel()
401
402class Input():
403
404 def __init__(self):
405 self.irc = None
406
407 def setBot(self, irc):
408 self.irc = irc
409
410 def read_keyboard(self):
411
412 command = ""
413 if self.irc is not None:
414 while not DONE:
415 command = sys.stdin.readline().lower().rstrip()
416 print command
417 if command == 'quit':
418 self.irc.send( 'QUIT' )
419 self.irc.socket.close()
420 self.irc.done = 1
421 DONE = 1
422 break;
423 elif command == 'viisaus':
424 self.irc.generate_markov()
425
426def main():
427
428 serial = SerialReader()
429 thread = threading.Thread(target=serial.read_from_port)
430 thread.daemon = 1
431 input = Input()
432 thread_input = threading.Thread(target=input.read_keyboard)
433 thread_input.daemon = 1
434 #logfile = "xcalibur.log"
435 logfile = None
436 if logfile is None:
437 markov = None
438 else:
439 markov = Markov(logfile)
440
441 irc = Ircbot(serial)
442 serial.setBot(irc)
443 input.setBot(irc)
444
445 TCPServer = newSocket()
446 TCPServer.setBot(irc)
447 socketThread = threading.Thread(target=TCPServer.run)
448 socketThread.daemon = 1
449
450 irc.connect()
451 thread.start()
452 thread_input.start()
453 socketThread.start()
454
455 signal.signal(signal.SIGINT, signal_handler)#signal_handler ottaa kiinni ctrl+c:n ja kill signaalin ja tekee DONE = 1, jolloin irc.mainloop() lopettaa looppaamisen
456
457 irc.mainloop()
458 try:
459 serial.stopTimer()
460 except:
461 print('serial timer could not be stopped')
462 try:
463 thread._Thread__stop()
464 except:
465 print(str(thread.getName()) + ' could not be terminated')
466 try:
467 thread_input._Thread__stop()
468 except:
469 print(str(thread_input.getName()) + ' could not be terminated')
470 try:
471 socketThread._Thread__stop()
472 except:
473 print "TCP serveriä ei saatu pysäytettyä"
474 try:
475 suljeClientit()
476 except:
477 print "Clienttejä ei saatu suljettua"
478 sys.exit()
479
480if __name__ == '__main__': main()