7 Module implementing the IRC channel widget. |
7 Module implementing the IRC channel widget. |
8 """ |
8 """ |
9 |
9 |
10 from __future__ import unicode_literals |
10 from __future__ import unicode_literals |
11 |
11 |
|
12 try: |
|
13 from itertools import izip_longest as zip_longest # __IGNORE_EXCEPTION__ |
|
14 except ImportError: |
|
15 from itertools import zip_longest |
|
16 |
12 import re |
17 import re |
13 |
18 |
14 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QDateTime, QPoint, QFileInfo, \ |
19 from PyQt5.QtCore import pyqtSlot, pyqtSignal, QDateTime, QPoint, QFileInfo, \ |
15 QTimer, QUrl |
20 QTimer, QUrl, QCoreApplication |
16 from PyQt5.QtGui import QIcon, QPainter, QTextCursor, QDesktopServices |
21 from PyQt5.QtGui import QIcon, QPainter, QTextCursor, QDesktopServices |
17 from PyQt5.QtWidgets import QWidget, QListWidgetItem, QMenu, QApplication, \ |
22 from PyQt5.QtWidgets import QWidget, QListWidgetItem, QMenu, QApplication, \ |
18 QInputDialog, QLineEdit |
23 QInputDialog, QLineEdit |
19 |
24 |
20 from E5Gui import E5MessageBox, E5FileDialog |
25 from E5Gui import E5MessageBox, E5FileDialog |
174 @return flag indicating that the topic can be changed (boolean) |
192 @return flag indicating that the topic can be changed (boolean) |
175 """ |
193 """ |
176 return(bool(self.__privilege & IrcUserItem.Operator) or |
194 return(bool(self.__privilege & IrcUserItem.Operator) or |
177 bool(self.__privilege & IrcUserItem.Admin) or |
195 bool(self.__privilege & IrcUserItem.Admin) or |
178 bool(self.__privilege & IrcUserItem.Owner)) |
196 bool(self.__privilege & IrcUserItem.Owner)) |
|
197 |
|
198 def setIgnored(self, ignored): |
|
199 """ |
|
200 Public method to set the user status to ignored. |
|
201 |
|
202 @param ignored flag indicating the new ignored status |
|
203 @type bool |
|
204 """ |
|
205 self.__ignored = ignored |
|
206 self.__setText() |
|
207 |
|
208 def isIgnored(self): |
|
209 """ |
|
210 Public method to check, if this user is ignored. |
|
211 |
|
212 @return flag indicating the ignored status |
|
213 @rtype bool |
|
214 """ |
|
215 return self.__ignored |
179 |
216 |
180 |
217 |
181 class IrcChannelWidget(QWidget, Ui_IrcChannelWidget): |
218 class IrcChannelWidget(QWidget, Ui_IrcChannelWidget): |
182 """ |
219 """ |
183 Class implementing the IRC channel widget. |
220 Class implementing the IRC channel widget. |
184 |
221 |
185 @signal sendData(str) emitted to send a message to the channel |
222 @signal sendData(str) emitted to send a message to the channel |
|
223 @signal sendCtcpRequest(str, str, str) emitted to send a CTCP request |
186 @signal sendCtcpReply(str, str) emitted to send a CTCP reply |
224 @signal sendCtcpReply(str, str) emitted to send a CTCP reply |
187 @signal channelClosed(str) emitted after the user has left the channel |
225 @signal channelClosed(str) emitted after the user has left the channel |
188 @signal openPrivateChat(str) emitted to open a "channel" for private |
226 @signal openPrivateChat(str) emitted to open a "channel" for private |
189 messages |
227 messages |
|
228 @signal awayCommand(str) emitted to set the away status via the /away |
|
229 command |
|
230 @signal leaveChannels(list) emitted to leave a list of channels |
|
231 @signal leaveAllChannels() emitted to leave all channels |
190 """ |
232 """ |
191 sendData = pyqtSignal(str) |
233 sendData = pyqtSignal(str) |
|
234 sendCtcpRequest = pyqtSignal(str, str, str) |
192 sendCtcpReply = pyqtSignal(str, str) |
235 sendCtcpReply = pyqtSignal(str, str) |
193 channelClosed = pyqtSignal(str) |
236 channelClosed = pyqtSignal(str) |
194 openPrivateChat = pyqtSignal(str) |
237 openPrivateChat = pyqtSignal(str) |
|
238 awayCommand = pyqtSignal(str) |
|
239 leaveChannels = pyqtSignal(list) |
|
240 leaveAllChannels = pyqtSignal() |
195 |
241 |
196 UrlRe = re.compile( |
242 UrlRe = re.compile( |
197 r"""((?:http|ftp|https):\/\/[\w\-_]+(?:\.[\w\-_]+)+""" |
243 r"""((?:http|ftp|https):\/\/[\w\-_]+(?:\.[\w\-_]+)+""" |
198 r"""(?:[\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)""") |
244 r"""(?:[\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)""") |
199 |
245 |
356 '<b>></b> {4}</font>'.format( |
402 '<b>></b> {4}</font>'.format( |
357 Preferences.getIrc("ChannelMessageColour"), |
403 Preferences.getIrc("ChannelMessageColour"), |
358 Preferences.getIrc("OwnNickColour"), |
404 Preferences.getIrc("OwnNickColour"), |
359 ircTimestamp(), self.__userName, |
405 ircTimestamp(), self.__userName, |
360 Utilities.html_encode(msg))) |
406 Utilities.html_encode(msg))) |
|
407 |
361 if msg.startswith("/"): |
408 if msg.startswith("/"): |
362 if self.__private: |
409 if self.__private: |
363 E5MessageBox.information( |
410 E5MessageBox.information( |
364 self, |
411 self, |
365 self.tr("Send Message"), |
412 self.tr("Send Message"), |
366 self.tr( |
413 self.tr( |
367 """Messages starting with a '/' are not allowed""" |
414 """Messages starting with a '/' are not allowed""" |
368 """ in private chats.""")) |
415 """ in private chats.""")) |
369 else: |
416 else: |
|
417 sendData = True |
|
418 # flag set to False, if command was handled |
|
419 |
370 msgList = msg.split() |
420 msgList = msg.split() |
371 cmd = msgList[0][1:].upper() |
421 cmd = msgList[0][1:].upper() |
372 if cmd == "MSG": |
422 if cmd in ["MSG", "QUERY"]: |
373 cmd = "PRIVMSG" |
423 cmd = "PRIVMSG" |
374 if len(msgList) > 1: |
424 if len(msgList) > 1: |
375 if msgList[1].strip().lower() in \ |
425 if msgList[1].strip().lower() in \ |
376 self.__serviceNamesLower: |
426 self.__serviceNamesLower: |
377 msg = "PRIVMSG " + \ |
427 msg = "PRIVMSG " + \ |
381 msg = "PRIVMSG {0} :{1}".format( |
431 msg = "PRIVMSG {0} :{1}".format( |
382 msgList[1], " ".join(msgList[2:])) |
432 msgList[1], " ".join(msgList[2:])) |
383 else: |
433 else: |
384 msgList[0] = cmd |
434 msgList[0] = cmd |
385 msg = " ".join(msgList) |
435 msg = " ".join(msgList) |
|
436 elif cmd == "NOTICE": |
|
437 if len(msgList) > 2: |
|
438 msg = "NOTICE {0} :{1}".format( |
|
439 msgList[1], " ".join(msgList[2:])) |
|
440 else: |
|
441 msg = "NOTICE {0}".format(" ".join(msgList[1:])) |
|
442 elif cmd == "PING": |
|
443 receiver = msgList[1] |
|
444 msg = "PING {0} " |
|
445 self.sendCtcpRequest.emit(receiver, "PING", "") |
|
446 sendData = False |
|
447 elif cmd == "IGNORE": |
|
448 sendData = False |
|
449 if len(msgList) > 1: |
|
450 if msgList[1] == "-r": |
|
451 ignored = False |
|
452 userNamesList = msgList[2:] |
|
453 else: |
|
454 ignored = True |
|
455 userNamesList = msgList[1:] |
|
456 else: |
|
457 userNamesList = [] |
|
458 userNames = ",".join( |
|
459 u.rstrip(",") for u in userNamesList).split(",") |
|
460 for userName in userNames: |
|
461 itm = self.__findUser(userName) |
|
462 if itm: |
|
463 itm.setIgnored(ignored) |
|
464 elif cmd == "UNIGNORE": |
|
465 sendData = False |
|
466 if len(msgList) > 1: |
|
467 userNamesList = msgList[1:] |
|
468 else: |
|
469 userNamesList = [] |
|
470 userNames = ",".join( |
|
471 u.rstrip(",") for u in userNamesList).split(",") |
|
472 for userName in userNames: |
|
473 itm = self.__findUser(userName) |
|
474 if itm: |
|
475 itm.setIgnored(False) |
|
476 elif cmd == "AWAY": |
|
477 sendData = False |
|
478 if len(msgList) > 1: |
|
479 msg = " ".join(msgList[1:]) |
|
480 else: |
|
481 msg = "" |
|
482 self.awayCommand.emit(msg) |
|
483 elif cmd == "JOIN": |
|
484 sendData = False |
|
485 if len(msgList) > 1: |
|
486 channels = msgList[1].split(",") |
|
487 if len(msgList) > 2: |
|
488 keys = msgList[2].split(",") |
|
489 else: |
|
490 keys = [] |
|
491 for channel, key in zip_longest( |
|
492 channels, keys, fillvalue=""): |
|
493 self.__ircWidget.joinChannel(channel, key) |
|
494 elif cmd == "PART": |
|
495 sendData = False |
|
496 if len(msgList) == 1: |
|
497 self.leaveChannel() |
|
498 else: |
|
499 self.leaveChannels.emit(msgList[1:]) |
|
500 elif cmd == "PARTALL": |
|
501 sendData = False |
|
502 self.leaveAllChannels.emit() |
386 else: |
503 else: |
387 msg = msg[1:] |
504 msg = msg[1:] |
388 self.sendData.emit(msg) |
505 if sendData: |
|
506 self.sendData.emit(msg) |
389 else: |
507 else: |
390 if self.__private: |
508 if self.__private: |
391 self.sendData.emit( |
509 self.sendData.emit( |
392 "PRIVMSG " + self.__privatePartner + " :" + msg) |
510 "PRIVMSG " + self.__privatePartner + " :" + msg) |
393 else: |
511 else: |
394 self.sendData.emit( |
512 self.sendData.emit( |
395 "PRIVMSG " + self.__name + " :" + msg) |
513 "PRIVMSG " + self.__name + " :" + msg) |
|
514 |
396 self.messageEdit.clear() |
515 self.messageEdit.clear() |
397 self.unsetMarkerLine() |
516 self.unsetMarkerLine() |
398 |
517 |
399 def requestLeave(self): |
518 def requestLeave(self): |
400 """ |
519 """ |
402 """ |
521 """ |
403 ok = E5MessageBox.yesNo( |
522 ok = E5MessageBox.yesNo( |
404 self, |
523 self, |
405 self.tr("Leave IRC channel"), |
524 self.tr("Leave IRC channel"), |
406 self.tr( |
525 self.tr( |
407 """Do you really want to leave the IRC channel <b>{0}</b>?""") |
526 """Do you really want to leave the IRC channel""" |
408 .format(self.__name)) |
527 """ <b>{0}</b>?""").format(self.__name)) |
409 if ok: |
528 if ok: |
410 if not self.__private: |
529 self.leaveChannel() |
411 self.sendData.emit( |
530 |
412 "PART " + self.__name + " :" + self.__partMessage) |
531 def leaveChannel(self): |
413 self.channelClosed.emit(self.__name) |
532 """ |
|
533 Public slot to leave the channel. |
|
534 """ |
|
535 if not self.__private: |
|
536 self.sendData.emit( |
|
537 "PART " + self.__name + " :" + self.__partMessage) |
|
538 self.channelClosed.emit(self.__name) |
414 |
539 |
415 def name(self): |
540 def name(self): |
416 """ |
541 """ |
417 Public method to get the name of the channel. |
542 Public method to get the name of the channel. |
418 |
543 |
513 # group(1) sender user name |
638 # group(1) sender user name |
514 # group(2) sender user@host |
639 # group(2) sender user@host |
515 # group(3) target nick |
640 # group(3) target nick |
516 # group(4) message |
641 # group(4) message |
517 if match.group(3).lower() == self.__name.lower(): |
642 if match.group(3).lower() == self.__name.lower(): |
|
643 senderName = match.group(1) |
|
644 itm = self.__findUser(senderName) |
|
645 if itm and itm.isIgnored(): |
|
646 # user should be ignored |
|
647 return True |
|
648 |
518 if match.group(4).startswith("\x01"): |
649 if match.group(4).startswith("\x01"): |
519 return self.__handleCtcp(match) |
650 return self.__handleCtcp(match) |
520 |
651 |
521 self.addMessage(match.group(1), match.group(4)) |
652 self.addMessage(senderName, match.group(4)) |
522 if self.__private and not self.topicLabel.text(): |
653 if self.__private and not self.topicLabel.text(): |
523 self.setPrivateInfo( |
654 self.setPrivateInfo( |
524 "{0} - {1}".format(match.group(1), match.group(2))) |
655 "{0} - {1}".format(match.group(1), match.group(2))) |
525 return True |
656 return True |
526 |
657 |