├── README.md └── PocketChatIRC.py /README.md: -------------------------------------------------------------------------------- 1 | # PocketChat 2 | PocketChat is a light weight IRC client that I wrote in python with a UI made for the PocketChip, but 3 | it can also be used on windows and linux. 4 | 5 | 6 | 7 | # COMMANDS: 8 | - Join a channel: /join #channel 9 | - Leave a channel: /leave in the window of the channel you want to leave 10 | - Send a pm: /msg user message 11 | - Change you nick: /nick newname 12 | - Close the client: /quit 13 | - Auto join channels by adding them to the box seperated by commas no spaces e.g.(#chipsters,#nextthingco,#linux) 14 | 15 | # Dependencies: 16 | 17 | - sudo apt-get install python3 18 | - sudo apt-get install python3-tk 19 | - run the program: 20 | python3 PocketChatIRC.py 21 | -------------------------------------------------------------------------------- /PocketChatIRC.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from tkinter import * 3 | from tkinter import messagebox, ttk 4 | from tkinter.scrolledtext import ScrolledText 5 | from socket import socket 6 | from select import select 7 | import sys, threading, time, ssl 8 | import configparser 9 | 10 | class Window(Frame): 11 | def __init__(self, master): 12 | Frame.__init__(self, master) 13 | self.root = master 14 | self.sckt = socket() 15 | self.parser = configparser.ConfigParser() 16 | self.parser.read('settings.ini') 17 | self.grid() 18 | self.tabs = {} 19 | self.init_window() 20 | self.getuser_popup() 21 | 22 | def getuser_popup(self):# Builds the UI for the username entry window 23 | self.checkSSL = IntVar() 24 | self.checkSSL.set(0) 25 | self.top = Toplevel() 26 | self.top.transient(root) 27 | w = 280 28 | h = 240 29 | sw = self.top.winfo_screenwidth() 30 | sh = self.top.winfo_screenheight() 31 | x = (sw - w)/2 32 | y = (sh - h)/2 33 | self.top.geometry('%dx%d+%d+%d' % (w, h, x, y)) 34 | 35 | self.SERVERIP = Entry(self.top, width=24) 36 | self.SERVERIP.place(x=75, y=10) 37 | self.SERVERIP.insert(0, 'weber.​freenode.​net') 38 | self.SERVERLB = Label(self.top, text = 'Server IP') 39 | self.SERVERLB.place(x=5, y=10) 40 | 41 | self.enterUser = Entry(self.top, width=24) 42 | self.enterUser.place(x=75, y=32) 43 | self.enterUser.focus_force() 44 | self.enterUsername = Label(self.top, text = 'Nickname') 45 | self.enterUsername.place(x=5, y=32) 46 | 47 | self.enterIDENT = Entry(self.top, width=24) 48 | self.enterIDENT.place(x=75, y=54) 49 | self.enterIDENTLb = Label(self.top, text = 'Identity') 50 | self.enterIDENTLb.place(x=5, y=54) 51 | 52 | self.enterREALNAME = Entry(self.top, width=24) 53 | self.enterREALNAME.place(x=75, y=76) 54 | self.enterREALNAMELb = Label(self.top, text = 'Realname') 55 | self.enterREALNAMELb.place(x=5, y=76) 56 | 57 | self.enterPASSWORD = Entry(self.top, show="*", width=24) 58 | self.enterPASSWORD.place(x=75, y=98) 59 | self.enterPASSWORDLb = Label(self.top, text = '*Password') 60 | self.enterPASSWORDLb.place(x=5, y=98) 61 | 62 | self.autoJoin = Entry(self.top, width=32) 63 | self.autoJoin.place(x=10, y=142) 64 | self.autoJoinLb = Label(self.top, text = '*Auto join channels seperated by a ","') 65 | self.autoJoinLb.place(x=5, y=120) 66 | 67 | self.sslCHECKBOX = Checkbutton(self.top, variable=self.checkSSL, onvalue=1, offvalue=0, text="Use SSL") 68 | self.sslCHECKBOX.place(x=5, y=160) 69 | self.sslCHECKBOX.select() 70 | 71 | self.OPTIONALLb = Label(self.top, text = '"*"optional sections') 72 | self.OPTIONALLb.place(x=140, y=160) 73 | 74 | self.usernameButton = Button(self.top, text='Connect', command = self.get_username, height=2, width=8) 75 | self.usernameButton.bind('', self.get_username) 76 | self.usernameButton.place(x=90, y=185) 77 | 78 | if self.parser.has_section("UserSettings"): 79 | self.enterUser.insert(0, self.parser.get('UserSettings', 'nickname')) 80 | self.enterIDENT.insert(0, self.parser.get('UserSettings', 'identity')) 81 | self.enterREALNAME.insert(0, self.parser.get('UserSettings', 'realname')) 82 | if self.parser.has_section("AutoJoin"): 83 | self.autoJoin.insert(0, self.parser.get('AutoJoin', 'channels')) 84 | 85 | def init_window(self):# Builds the UI for the main window 86 | self.n = ttk.Notebook(root) 87 | self.n.enable_traversal() 88 | root.title('Python Chat') 89 | w = 480 90 | h = 272 91 | sw = root.winfo_screenwidth() 92 | sh = root.winfo_screenheight() 93 | x = (sw - w)/2 94 | y = (sh - h)/2 95 | root.geometry('%dx%d+%d+%d' % (w, h, x, y)) 96 | 97 | self.textboxframe = ttk.Frame(self.n) 98 | self.textboxframe.grid(row=0, column=0, sticky=N+S+E+W) 99 | 100 | self.textReceive = ScrolledText(self.textboxframe, height=24, width=47, wrap = WORD) 101 | self.textReceive.grid(row = 0, column= 0, padx=(10,0), pady=(10,5), sticky=N+S+E+W) 102 | self.textReceive.config(state=DISABLED) 103 | 104 | self.textEntry = ScrolledText(self.textboxframe, height=2, width=47, wrap = WORD) 105 | self.textEntry.grid(row = 2, column= 0, padx=(10,0), pady=(0,10), sticky=N+S+E+W) 106 | self.textEntry.bind('', self.check_pm_commands) 107 | 108 | Grid.rowconfigure(root, 0, weight=1) 109 | Grid.columnconfigure(root, 0, weight=1) 110 | Grid.rowconfigure(self.textboxframe, 0, weight=1) 111 | Grid.columnconfigure(self.textboxframe, 0, weight=1) 112 | 113 | self.tabs['Server Info'] = {} 114 | self.tabs['Server Info']['tab'] = self.textboxframe 115 | self.tabs['Server Info']['textbox'] = self.textReceive 116 | self.tabs['Server Info']['entrybox'] = self.textEntry 117 | self.tabs['Server Info']['onlineusers'] = '' 118 | 119 | self.n.add(self.textboxframe, text='Server Info') 120 | self.n.grid(row=0, column=0, sticky=N+S+E+W) 121 | 122 | def get_username(self, event=None):# Gets the initial username after hitting the enter chat button 123 | self.aliasName = self.enterUser.get() 124 | self.IDENT = self.enterIDENT.get() 125 | self.REALNAME = self.enterREALNAME.get() 126 | self.SERVER = self.SERVERIP.get() 127 | password = self.enterPASSWORD.get() 128 | self.Channels = self.autoJoin.get() 129 | if self.aliasName == '': 130 | messagebox.showinfo(message='You must enter a username', icon='warning') 131 | elif ' ' in self.aliasName: 132 | messagebox.showinfo(message='Username cannot contain spaces', icon='warning') 133 | elif not password and self.checkSSL.get() == 1: 134 | self.sckt = ssl.wrap_socket(self.sckt) 135 | self.PORT = 6697 136 | self.start_recv_loop() 137 | elif password and self.checkSSL.get() == 1: 138 | self.sckt = ssl.wrap_socket(self.sckt) 139 | self.PORT = 6697 140 | self.start_recv_loop() 141 | self.sckt.send(bytes('PRIVMSG NickServ : IDENTIFY %s\r\n' % password, "UTF-8")) 142 | else: 143 | self.PORT = 6667 144 | self.start_recv_loop() 145 | self.user_settings() 146 | 147 | def start_recv_loop(self): 148 | self.top.destroy() 149 | self.master.title('Python Chat - %s' % self.aliasName) 150 | self.sckt.connect((self.SERVER, self.PORT)) 151 | thread = threading.Thread(target=self.recv_loop, args=[self.sckt]) 152 | thread.daemon = True 153 | thread.start() 154 | self.root.after(500, lambda:self.sckt.send(bytes("NICK %s\r\n" % self.aliasName, "UTF-8"))) 155 | self.root.after(500, lambda:self.sckt.send(bytes("USER %s %s bla :%s\r\n" % (self.IDENT, self.SERVER, self.REALNAME), "UTF-8"))) 156 | self.textEntry.focus_force() 157 | self.root.after(8000, self.auto_join_chan) 158 | 159 | def check_pm_commands(self, event=None): 160 | tab = self.n.tab(self.n.select(), "text") 161 | textboxinc = self.tabs[tab]['textbox'] 162 | message = self.tabs[tab]['entrybox'].get('1.0','end-1c') 163 | self.tabs[tab]['entrybox'].delete('1.0', END) 164 | self.tabs[tab]['entrybox'].focus_force() 165 | if len(message)>=1 and message[0] == '/': 166 | self.process_commands(message) 167 | else: 168 | self.post_pm_controls(self.aliasName + ':>' + message + '\n', textboxinc)# Post the received text to the window 169 | self.sckt.send(bytes("PRIVMSG %s %s \r\n" % (tab, ':' + message), "UTF-8")) 170 | return 'break' 171 | 172 | def process_commands(self, message): 173 | format = message.split(' ') 174 | if '/msg' in format: 175 | type = 'pm' 176 | message = " ".join(format[2:]) 177 | tab_name = format[1].lower() 178 | if len(message) < 1: 179 | messagebox.showinfo(message='You must enter some text after /msg username', icon='warning') 180 | else: 181 | self.add_tab(tab_name, type) 182 | self.tabs[tab_name]['entrybox'].delete('1.0', END) 183 | self.find_window(tab_name, self.aliasName + ':>' + message + '\n') 184 | self.sckt.send(bytes("PRIVMSG %s %s \r\n" % (tab_name, ':' + message), "UTF-8")) 185 | elif '/join' in message: 186 | tab_name = format[1].lower() 187 | type = 'channel' 188 | self.add_tab(tab_name, type) 189 | elif '/leave' in message: 190 | self.leave_channel() 191 | elif '/nick' in message: 192 | self.name_change(self.aliasName, message) 193 | elif '/quit' in message: 194 | self.shutdownClient() 195 | else: 196 | messagebox.showinfo(message='Command not found.', icon='warning') 197 | 198 | def recv_loop(self, connection): # Main receiving loop for all incoming messages 199 | while True: 200 | (readable, writable, errored) = select([connection], [], [connection], 0.1) 201 | if readable: 202 | readbuffer="" 203 | readbuffer=readbuffer+self.sckt.recv(1024).decode("UTF-8") 204 | temp=str.split(readbuffer, "\n") 205 | readbuffer=temp.pop( ) 206 | for line in temp: 207 | line=str.rstrip(line) 208 | line=str.split(line) 209 | self.root.after_idle(self.iterate_through_incoming, line) 210 | 211 | def iterate_through_incoming(self, line): # Look through the incoming messages for info from the server 212 | try: 213 | if line[0] == "PING":# Looks for PING from the server and replies with a PONG 214 | self.sckt.send(bytes("PONG %s\r\n" % line[1], "UTF-8")) 215 | elif self.aliasName + '!' in line[0]: 216 | pass 217 | elif line[0] == '353' or line[1] == '353': # Looks for NAMES list 218 | self.build_online_list(line) 219 | elif line[1] == '401': # Returns warning if user/channel doesn't exist 220 | messagebox.showinfo(message='No such user/channel %s'% line[3], icon='warning') 221 | elif line[1] == 'QUIT': 222 | self.remove_on_quit(line) 223 | elif line[1] == "PRIVMSG" and line[3] == ':\x01ACTION': 224 | message = line[3].lstrip(':') 225 | x = " ".join(line[4:]) # Grabbing everything after the 3rd index and joining the message. 226 | format_user1 = line[0].strip(':') 227 | finish_format = format_user1.split('!') 228 | user = finish_format[0] 229 | self.find_window(line[2].lower(), '*' + user + ' ' + x + '\n') # Post the received text to the window 230 | elif line[1] == "PRIVMSG": # If PRIVMSG is in line[1] position, its a message from a channel or a private message 231 | self.get_incoming_channel(line) 232 | elif line[1] == 'JOIN' and self.aliasName not in line[0]:# If JOIN is line[1], a new user joined the channel 233 | self.get_join_leave_name(line) 234 | elif line[1] == 'PART' and self.aliasName not in line[0]: # If PART is line[1] a user has left update the online user list 235 | self.get_join_leave_name(line) 236 | elif line[1] == 'NICK' and self.aliasName not in line[2]: # If NICK is line[1] a user has changed their name, update the online user list 237 | self.get_join_leave_name(line) 238 | elif line[1] == 'NOTICE' and line[2] == self.aliasName: # If line[1] is NOTICE and line[2] is the users name grab that message 239 | get_tab = line[3].split('[') 240 | get_tab_finish = get_tab[1].split(']') 241 | x = " ".join(line[4:]) # Grabbing everything after the 3rd index and join the message. 242 | self.find_window(get_tab_finish[0].lower(), x + '\n') 243 | elif line[1] == '328' or line[1] == '332' or line[1] == '333' or line[1] == '366': # Grabs server messages on joining. If the tab name is in line[3], grab the message 244 | x = " ".join(line[3:]) # Grabbing everything after the 3rd index and join the message. 245 | self.find_window(line[3].lower(), x + '\n') 246 | elif ':' in line[0]: # Grabs all message from the server and posts them to the main tab 247 | x = " ".join(line[3:]) # Grabbing everything after the 1st index and join the message. 248 | self.post_pm_controls(x + '\n', self.tabs['Server Info']['textbox']) 249 | else: # Grabs names that come through as a seperate list and rebuilds it and runs it back through this method to be reiterated 250 | tab = self.n.tab(self.n.select(), "text") 251 | strayusers = [':placeholder', '353', self.aliasName, '=', tab] 252 | for item in line: 253 | strayusers.append(item) 254 | self.iterate_through_incoming(strayusers) 255 | except IndexError: # Some times there is no data in the indexes of the incoming messages 256 | pass 257 | 258 | def get_incoming_channel(self, line): 259 | if "#" in line[2]: # Message from channel 260 | channel = "" # Get the incoming sender 261 | incomg_msg = line[0].split('!') # Split the message at the ! 262 | channel += incomg_msg[0].lstrip(':') # Strip the : out 263 | x = " ".join(line[3:]) # Grabbing everything after the 3rd index and join the message. 264 | self.find_window(line[2].lower(), channel+':>'+ x.lstrip(':') + '\n') # Post the received text to the window 265 | else: # Private message from user 266 | type = 'pm' 267 | message = line[3].lstrip(':') 268 | x = " ".join(line[4:]) # Grabbing everything after the 3rd index and joining the message. 269 | format_sender1 = line[0].strip(':') 270 | finish_format = format_sender1.split('!') 271 | user = finish_format[0] 272 | self.add_tab(user.lower(), type) 273 | self.find_window(user.lower(), user + ':>' + message + ' ' + x + '\n') 274 | 275 | def add_tab(self, tab_name, type): 276 | if tab_name not in self.tabs: 277 | self.tab_generator(tab_name, type) 278 | else: 279 | if tab_name == self.n.tab(self.tabs[tab_name]['tab'], "text") and self.n.tab(self.tabs[tab_name]['tab'], "state") == 'hidden' and type == 'channel': 280 | self.n.tab(self.tabs[tab_name]['tab'], state='normal') 281 | self.n.select(self.tabs[tab_name]['tab']) 282 | self.tabs[tab_name]['onlineusers'].delete(1, 'end') 283 | self.tabs[tab_name]['textbox'].delete(0,'end-1c') 284 | self.tabs[tab_name]['entrybox'].focus_force() 285 | self.sckt.send(bytes("JOIN %s\r\n" % tab_name, "UTF-8")) 286 | elif tab_name == self.n.tab(self.tabs[tab_name]['tab'], "text") and self.n.tab(self.tabs[tab_name]['tab'], "state") == 'normal' and type == 'channel': 287 | self.n.select(self.tabs[tab_name]['tab']) 288 | self.tabs[tab_name]['entrybox'].focus_force() 289 | 290 | def tab_generator(self, tab_name, type): # Tab generator for creating channels/pms 291 | # Create channel tab 292 | self.tab_name = ttk.Frame(self.n) 293 | self.tab_name.grid(row=0, column=0, rowspan=2, sticky=N+S+E+W) 294 | 295 | self.receive_user = ScrolledText(self.tab_name, height=24, width=47, wrap = WORD) 296 | self.receive_user.grid(row=0, column=0, padx=(10,0), pady=(10,5), sticky=N+S+E+W) 297 | self.receive_user.config(state=DISABLED) 298 | 299 | self.pm_Entry = ScrolledText(self.tab_name, height=2, width=47, wrap = WORD) 300 | self.pm_Entry.grid(row=2, column=0, padx=(10,0), pady=(0,10), sticky=N+S+E+W) 301 | self.pm_Entry.bind('', self.check_pm_commands) 302 | 303 | if type == 'channel': 304 | self.pm_users_box = Listbox(self.tab_name, width=12) 305 | self.pm_users_box.grid(row = 0, column= 1, rowspan=3, padx=(0,10), pady=(10,10), sticky=N+S+E+W) 306 | self.pm_users_box.insert(0, 'Online [0]') 307 | 308 | self.tabs[tab_name] = {} 309 | self.tabs[tab_name]['tab'] = self.tab_name 310 | self.tabs[tab_name]['textbox'] = self.receive_user 311 | self.tabs[tab_name]['entrybox'] = self.pm_Entry 312 | self.tabs[tab_name]['onlineusers'] = self.pm_users_box 313 | self.sckt.send(bytes("JOIN %s\r\n" % tab_name, "UTF-8")) 314 | else: 315 | self.pm_Close = Button(self.tab_name, width=7, text='Close tab', command=lambda:self.remove_on_close()) 316 | self.pm_Close.grid(row=0, column=1, padx=(5,5), pady=(5,150), sticky=N+S+E+W) 317 | 318 | self.tabs[tab_name] = {} 319 | self.tabs[tab_name]['tab'] = self.tab_name 320 | self.tabs[tab_name]['textbox'] = self.receive_user 321 | self.tabs[tab_name]['entrybox'] = self.pm_Entry 322 | self.tabs[tab_name]['onlineusers'] = '' 323 | 324 | Grid.rowconfigure(self.tab_name, 0, weight=1) 325 | Grid.columnconfigure(self.tab_name, 0, weight=1) 326 | 327 | self.n.add(self.tab_name, text = tab_name) 328 | self.n.select(self.tab_name) 329 | self.pm_Entry.focus_force() 330 | 331 | def remove_on_close(self): # Get the current active tab and close it on click 332 | current_tab = self.n.tab(self.n.select(), "text") 333 | if current_tab in self.tabs and current_tab != self.SERVER: 334 | if self.tabs[current_tab]['onlineusers'] == '': 335 | self.n.hide(self.n.select()) 336 | current_tab = self.n.tab(self.n.select(), "text") 337 | self.tabs[current_tab]['entrybox'].focus_force() 338 | else: 339 | self.n.hide(self.n.select()) 340 | current_tab = self.n.tab(self.n.select(), "text") 341 | self.tabs[current_tab]['entrybox'].focus_force() 342 | 343 | def find_window(self, tab_name, message): # Get the name of the tab so the message gets to the correct tab 344 | if tab_name == self.n.tab(self.tabs[tab_name]['tab'], "text"): 345 | if "No such channel" in self.tabs[tab_name]['textbox'].get("1.0",'end-1c'): 346 | pass 347 | else: 348 | self.n.tab(self.tabs[tab_name]['tab'], state='normal') # If the incoming user in the the current tabs change the state of the tab to normal if hidden 349 | self.post_pm_controls(message, self.tabs[tab_name]['textbox'])# Post the received text to the window 350 | 351 | def name_change(self, user, line): # Handles a name change if a user in the channel changes their name. 352 | tab_storage = [] 353 | for tabs in self.tabs: 354 | if self.tabs[tabs]['onlineusers'] == '': 355 | pass 356 | else: 357 | tab_storage.append(tabs) 358 | for tabs in tab_storage: 359 | for item in self.tabs[tabs]['onlineusers'].get(0, 'end'): 360 | if user == item: 361 | incbox = self.tabs[tabs]['textbox'] 362 | new_user = line[2].split(':') 363 | index = self.tabs[tabs]['onlineusers'].get(0, END).index(user) 364 | self.tabs[tabs]['onlineusers'].delete(index) 365 | if user in item and user == self.aliasName: 366 | new_user = line.split(' ') 367 | self.tabs[tabs]['onlineusers'].insert(END, new_user[1]) 368 | self.post_pm_controls(('*You are now known as %s' % new_user[1]) + '\n', incbox) 369 | self.aliasName = new_user[1] 370 | self.sckt.send(bytes("NICK %s\r\n" % self.aliasName, "UTF-8")) 371 | else: 372 | self.tabs[tabs]['onlineusers'].insert(END, new_user[1]) 373 | self.post_pm_controls('*User %s is now known as %s' % (user, new_user[1]) + '\n', incbox) 374 | 375 | def get_join_leave_name(self, line): # Gets the name of the user if they join, leave, or change their name 376 | tab = line[2].split(':') # Format the incoming name and remove the ":" and get the channel 377 | user = "" # Temp storage for incoming sender name 378 | incomg_msg = line[0].split('!') # Split is at the '!' 379 | user += incomg_msg[0].lstrip(":")# Remove the ':' from the string 380 | if(line[1] == 'JOIN'): # Check if the message is someone joining 381 | self.add_online_user(user, tab[1]) 382 | elif(line[1] == 'PART'): # Check if the message is someone leaving 383 | self.remove_online_user(user, tab[0]) 384 | elif(line[1] == 'NICK'): # Check is the message is someone changing their name 385 | self.name_change(user, line) 386 | 387 | def add_online_user(self, user, tab): # Adds users as they join 388 | inctab = self.tabs[tab]['textbox'] # Tells which tab to update 389 | self.tabs[tab]['onlineusers'].insert(END, user) # Add the new user to the online user Listbox 390 | self.post_pm_controls('*User %s has joined the channel' % user + '\n', inctab) # Post a message to the window that a user had joined 391 | 392 | def remove_online_user(self, user, tab): # Remove users as they leave an active channel 393 | inctab = self.tabs[tab.lower()]# Tells which tab to update 394 | try: 395 | index = inctab['onlineusers'].get(0,'end').index(user) 396 | inctab['onlineusers'].delete(index) # Try to remove the user from the onlinelist and post who left 397 | self.post_pm_controls('User %s has left the channel' % user + '\n', inctab['textbox']) 398 | except ValueError: # If the user's not in the dictionary list, post who left anyway 399 | self.post_pm_controls('*User %s has left the channel' % user + '\n', inctab['textbox']) 400 | try: 401 | index1 = inctab['onlineusers'].get(0,'end').index('@' + user) # For getting the index if the user is admin 402 | inctab['onlineusers'].delete(index1) 403 | self.post_pm_controls('*User %s has left the channel' % user + '\n', inctab['textbox']) 404 | except ValueError: # If the user's not in the dictionary list, post who left anyway 405 | pass 406 | 407 | def remove_on_quit(self, line): # Remove users as they quit an active channel 408 | tab_storage = [] 409 | format = line[0].split('!') 410 | user = format[0].strip(':') 411 | for tabs in self.tabs: 412 | if self.tabs[tabs]['onlineusers'] == '': 413 | pass 414 | else: 415 | tab_storage.append(tabs) 416 | for tabs in tab_storage: 417 | for item in self.tabs[tabs]['onlineusers'].get(0, 'end'): 418 | if user == item: 419 | incbox = self.tabs[tabs]['textbox'] 420 | index = self.tabs[tabs]['onlineusers'].get(0, END).index(user) 421 | self.tabs[tabs]['onlineusers'].delete(index) 422 | x = " ".join(line[2:]) 423 | self.post_pm_controls('*User %s has quit. Reason: %s' % (user, x) + '\n', incbox) 424 | 425 | def leave_channel(self): # Called on /leave from user. Leaves the current tab that is focused 426 | tab = self.n.tab(self.n.select(), "text") 427 | if tab == self.SERVER: 428 | pass 429 | elif "No such channel" in self.tabs[tab]['textbox'].get("1.0",'end-1c'): 430 | self.remove_on_close() 431 | elif self.tabs[tab]['onlineusers'] == '': 432 | self.remove_on_close() 433 | else: 434 | self.sckt.send(bytes("PART %s\r\n" % tab, "UTF-8")) 435 | self.remove_on_close() 436 | 437 | def build_online_list(self, line):# Builds the online users list 438 | try: 439 | if self.tabs[line[4].lower()]['onlineusers'] == '': # If there are no users in that channel/users dictionary, pass 440 | pass 441 | else: 442 | users = [] # Temp storage for incoming names 443 | first_user = line[5].replace(':', '') # Formats the first username in the list 444 | users.append(first_user) # add user to temp list 445 | for items in line[6:]: # Get the rest of the incoming name and add them to the dictionaries users list 446 | users.append(items) 447 | for items in sorted(users): 448 | self.tabs[line[4].lower()]['onlineusers'].insert(END, items) 449 | self.count_online(line[4].lower()) 450 | except KeyError: 451 | pass 452 | 453 | def count_online(self, tab_name): # counts the online users. 454 | user_count = self.tabs[tab_name]['onlineusers'].size() 455 | self.tabs[tab_name]['onlineusers'].delete(0) 456 | self.tabs[tab_name]['onlineusers'].insert(0, 'Online [%d]' % (user_count - 1)) 457 | 458 | def post_pm_controls(self, pm, window):# Handles the state of the tabed text boxes as well as inserting text into the box 459 | window.config(state=NORMAL) 460 | window.insert(END, time.strftime("[%I:%M %p]") + pm) 461 | window.config(state=DISABLED) 462 | window.see(END) 463 | 464 | def shutdownClient(self): # Called on /quit from the user. 465 | for i in self.n.tabs(): 466 | self.sckt.send(bytes("PART %s\r\n" % self.n.tab(i, "text"), "UTF-8")) 467 | self.root.destroy() 468 | 469 | def auto_join_chan(self): 470 | if self.Channels: 471 | get_channels = self.Channels.split(',') 472 | for item in get_channels: 473 | self.add_tab(item, 'channel') 474 | 475 | def user_settings(self): 476 | if not self.parser.has_section("UserSettings"): 477 | self.parser.add_section('UserSettings') 478 | self.parser.set('UserSettings', 'NickName', self.aliasName) 479 | self.parser.set('UserSettings', 'Identity', self.IDENT) 480 | self.parser.set('UserSettings', 'RealName', self.REALNAME) 481 | if not self.parser.has_section('AutoJoin'): 482 | self.parser.add_section('AutoJoin') 483 | self.parser.set('AutoJoin', 'Channels', self.Channels) 484 | with open("settings.ini", "w") as config: 485 | self.parser.write(config) 486 | 487 | if __name__ == '__main__': 488 | root = Tk() 489 | app = Window(root) 490 | root.mainloop() 491 | --------------------------------------------------------------------------------