|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 - 2025 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing functions used by several IRC objects. |
|
8 """ |
|
9 |
|
10 import re |
|
11 |
|
12 from PyQt6.QtCore import QCoreApplication, QTime |
|
13 from PyQt6.QtWidgets import QApplication |
|
14 |
|
15 from eric7 import EricUtilities |
|
16 from PluginExtensionIrc import ircExtensionPluginObject |
|
17 |
|
18 __UrlRe = re.compile( |
|
19 r"""((?:http|ftp|https):\/\/[\w\-_]+(?:\.[\w\-_]+)+""" |
|
20 r"""(?:[\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)""" |
|
21 ) |
|
22 __ColorRe = re.compile( |
|
23 r"""((?:\x03(?:0[0-9]|1[0-5]|[0-9])?(?:,(?:0[0-9]|1[0-5]|[0-9]))?)""" |
|
24 r"""|\x02|\x03|\x13|\x15|\x16|\x17|\x1d|\x1f)""" |
|
25 ) |
|
26 |
|
27 |
|
28 def ircTimestamp(): |
|
29 """ |
|
30 Module method to generate a time stamp string. |
|
31 |
|
32 @return time stamp |
|
33 @rtype str |
|
34 """ |
|
35 if ircExtensionPluginObject.getPreferences("ShowTimestamps"): |
|
36 if ircExtensionPluginObject.getPreferences("TimestampIncludeDate"): |
|
37 if QApplication.isLeftToRight(): |
|
38 f = "{0} {1}" |
|
39 else: |
|
40 f = "{1} {0}" |
|
41 formatString = f.format( |
|
42 ircExtensionPluginObject.getPreferences("DateFormat"), |
|
43 ircExtensionPluginObject.getPreferences("TimeFormat"), |
|
44 ) |
|
45 else: |
|
46 formatString = ircExtensionPluginObject.getPreferences("TimeFormat") |
|
47 return '<font color="{0}">[{1}]</font> '.format( |
|
48 ircExtensionPluginObject.getPreferences("TimestampColour"), |
|
49 QTime.currentTime().toString(formatString), |
|
50 ) |
|
51 else: |
|
52 return "" |
|
53 |
|
54 |
|
55 def ircFilter(msg): |
|
56 """ |
|
57 Module method to make the message HTML compliant and detect URLs. |
|
58 |
|
59 @param msg message to process |
|
60 @type str |
|
61 @return processed message |
|
62 @rtype str |
|
63 """ |
|
64 # step 1: cleanup message |
|
65 msg = EricUtilities.html_encode(msg) |
|
66 |
|
67 # step 2: replace IRC formatting characters |
|
68 openTags = [] |
|
69 parts = __ColorRe.split(msg) |
|
70 msgParts = [] |
|
71 for part in parts: |
|
72 if part == "\x02": # bold |
|
73 if openTags and openTags[-1] == "b": |
|
74 msgParts.append("</" + openTags.pop(-1) + ">") |
|
75 else: |
|
76 msgParts.append("<b>") |
|
77 openTags.append("b") |
|
78 elif part in ["\x03", "\x17"]: |
|
79 if ircExtensionPluginObject.getPreferences("EnableIrcColours"): |
|
80 if openTags and openTags[-1] == "span": |
|
81 msgParts.append("</" + openTags.pop(-1) + ">") |
|
82 else: |
|
83 continue |
|
84 else: |
|
85 continue |
|
86 elif part == "\x0f": # reset |
|
87 while openTags: |
|
88 msgParts.append("</" + openTags.pop(-1) + ">") |
|
89 elif part == "\x13": # strikethru |
|
90 if openTags and openTags[-1] == "s": |
|
91 msgParts.append("</" + openTags.pop(-1) + ">") |
|
92 else: |
|
93 msgParts.append("<s>") |
|
94 openTags.append("s") |
|
95 elif part in ["\x15", "\x1f"]: # underline |
|
96 if openTags and openTags[-1] == "u": |
|
97 msgParts.append("</" + openTags.pop(-1) + ">") |
|
98 else: |
|
99 msgParts.append("<u>") |
|
100 openTags.append("u") |
|
101 elif part == "\x16": |
|
102 # revert color not supported |
|
103 continue |
|
104 elif part == "\x1d": # italic |
|
105 if openTags and openTags[-1] == "i": |
|
106 msgParts.append("</" + openTags.pop(-1) + ">") |
|
107 else: |
|
108 msgParts.append("<i>") |
|
109 openTags.append("i") |
|
110 elif part.startswith("\x03"): |
|
111 if ircExtensionPluginObject.getPreferences("EnableIrcColours"): |
|
112 colors = part[1:].split(",", 1) |
|
113 if len(colors) == 1: |
|
114 # foreground color only |
|
115 tag = '<span style="color:{0}">'.format( |
|
116 ircExtensionPluginObject.getPreferences( |
|
117 "IrcColor{0}".format(int(colors[0])) |
|
118 ) |
|
119 ) |
|
120 else: |
|
121 if colors[0]: |
|
122 # foreground and background |
|
123 tag = '<span style="background-color:{0};color={1}">'.format( |
|
124 ircExtensionPluginObject.getPreferences( |
|
125 "IrcColor{0}".format(int(colors[0])) |
|
126 ), |
|
127 ircExtensionPluginObject.getPreferences( |
|
128 "IrcColor{0}".format(int(colors[1])) |
|
129 ), |
|
130 ) |
|
131 else: |
|
132 # background only |
|
133 tag = '<span style="background-color:{0}">'.format( |
|
134 ircExtensionPluginObject.getPreferences( |
|
135 "IrcColor{0}".format(int(colors[1])) |
|
136 ) |
|
137 ) |
|
138 msgParts.append(tag) |
|
139 openTags.append("span") |
|
140 else: |
|
141 continue |
|
142 else: |
|
143 msgParts.append(part) |
|
144 msg = "".join(msgParts) |
|
145 |
|
146 # step 3: find http and https links |
|
147 parts = __UrlRe.split(msg) |
|
148 msgParts = [] |
|
149 for part in parts: |
|
150 if part.startswith(("http://", "https://", "ftp://")): |
|
151 msgParts.append( |
|
152 '<a href="{0}" style="color:{1}">{0}</a>'.format( |
|
153 part, ircExtensionPluginObject.getPreferences("HyperlinkColour") |
|
154 ) |
|
155 ) |
|
156 else: |
|
157 msgParts.append(part) |
|
158 |
|
159 return "".join(msgParts) |
|
160 |
|
161 |
|
162 __channelModesDict = None |
|
163 |
|
164 |
|
165 def __initChannelModesDict(): |
|
166 """ |
|
167 Private module function to initialize the channels modes dictionary. |
|
168 """ |
|
169 global __channelModesDict |
|
170 |
|
171 modesDict = { |
|
172 "a": QCoreApplication.translate("IrcUtilities", "anonymous"), |
|
173 "b": QCoreApplication.translate("IrcUtilities", "ban mask"), |
|
174 "c": QCoreApplication.translate("IrcUtilities", "no colors allowed"), |
|
175 "e": QCoreApplication.translate("IrcUtilities", "ban exception mask"), |
|
176 "i": QCoreApplication.translate("IrcUtilities", "invite only"), |
|
177 "k": QCoreApplication.translate("IrcUtilities", "password protected"), |
|
178 "l": QCoreApplication.translate("IrcUtilities", "user limit"), |
|
179 "m": QCoreApplication.translate("IrcUtilities", "moderated"), |
|
180 "n": QCoreApplication.translate("IrcUtilities", "no messages from outside"), |
|
181 "p": QCoreApplication.translate("IrcUtilities", "private"), |
|
182 "q": QCoreApplication.translate("IrcUtilities", "quiet"), |
|
183 "r": QCoreApplication.translate("IrcUtilities", "reop channel"), |
|
184 "s": QCoreApplication.translate("IrcUtilities", "secret"), |
|
185 "t": QCoreApplication.translate("IrcUtilities", "topic protection"), |
|
186 "I": QCoreApplication.translate("IrcUtilities", "invitation mask"), |
|
187 } |
|
188 __channelModesDict = modesDict |
|
189 |
|
190 |
|
191 def getChannelModesDict(): |
|
192 """ |
|
193 Module function to get the dictionary with the channel modes mappings. |
|
194 |
|
195 @return dictionary with channel modes mapping |
|
196 @rtype dict |
|
197 """ |
|
198 global __channelModesDict |
|
199 |
|
200 if __channelModesDict is None: |
|
201 __initChannelModesDict() |
|
202 |
|
203 return __channelModesDict |