line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package IRC::Utils; |
2
|
|
|
|
|
|
|
BEGIN { |
3
|
2
|
|
|
2
|
|
29196
|
$IRC::Utils::AUTHORITY = 'cpan:HINRIK'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
BEGIN { |
6
|
2
|
|
|
2
|
|
39
|
$IRC::Utils::VERSION = '0.12'; |
7
|
|
|
|
|
|
|
} |
8
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
17
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
64
|
|
10
|
2
|
|
|
2
|
|
9
|
use warnings FATAL => 'all'; |
|
2
|
|
|
|
|
13
|
|
|
2
|
|
|
|
|
101
|
|
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
889
|
use Encode qw(decode); |
|
2
|
|
|
|
|
11268
|
|
|
2
|
|
|
|
|
127
|
|
13
|
2
|
|
|
2
|
|
12945
|
use Encode::Guess; |
|
2
|
|
|
|
|
14948
|
|
|
2
|
|
|
|
|
16
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
require Exporter; |
16
|
2
|
|
|
2
|
|
154
|
use base qw(Exporter); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
781
|
|
17
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
18
|
|
|
|
|
|
|
uc_irc lc_irc parse_mode_line normalize_mask matches_mask matches_mask_array |
19
|
|
|
|
|
|
|
unparse_mode_line gen_mode_change parse_user is_valid_nick_name eq_irc |
20
|
|
|
|
|
|
|
decode_irc is_valid_chan_name has_color has_formatting strip_color |
21
|
|
|
|
|
|
|
strip_formatting NORMAL BOLD UNDERLINE REVERSE ITALIC FIXED WHITE BLACK |
22
|
|
|
|
|
|
|
BLUE GREEN RED BROWN PURPLE ORANGE YELLOW LIGHT_GREEN TEAL LIGHT_CYAN |
23
|
|
|
|
|
|
|
LIGHT_BLUE PINK GREY LIGHT_GREY numeric_to_name name_to_numeric |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( ALL => [@EXPORT_OK] ); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use constant { |
28
|
|
|
|
|
|
|
# cancel all formatting and colors |
29
|
2
|
|
|
|
|
8868
|
NORMAL => "\x0f", |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# formatting |
32
|
|
|
|
|
|
|
BOLD => "\x02", |
33
|
|
|
|
|
|
|
UNDERLINE => "\x1f", |
34
|
|
|
|
|
|
|
REVERSE => "\x16", |
35
|
|
|
|
|
|
|
ITALIC => "\x1d", |
36
|
|
|
|
|
|
|
FIXED => "\x11", |
37
|
|
|
|
|
|
|
BLINK => "\x06", |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# mIRC colors |
40
|
|
|
|
|
|
|
WHITE => "\x0300", |
41
|
|
|
|
|
|
|
BLACK => "\x0301", |
42
|
|
|
|
|
|
|
BLUE => "\x0302", |
43
|
|
|
|
|
|
|
GREEN => "\x0303", |
44
|
|
|
|
|
|
|
RED => "\x0304", |
45
|
|
|
|
|
|
|
BROWN => "\x0305", |
46
|
|
|
|
|
|
|
PURPLE => "\x0306", |
47
|
|
|
|
|
|
|
ORANGE => "\x0307", |
48
|
|
|
|
|
|
|
YELLOW => "\x0308", |
49
|
|
|
|
|
|
|
LIGHT_GREEN => "\x0309", |
50
|
|
|
|
|
|
|
TEAL => "\x0310", |
51
|
|
|
|
|
|
|
LIGHT_CYAN => "\x0311", |
52
|
|
|
|
|
|
|
LIGHT_BLUE => "\x0312", |
53
|
|
|
|
|
|
|
PINK => "\x0313", |
54
|
|
|
|
|
|
|
GREY => "\x0314", |
55
|
|
|
|
|
|
|
LIGHT_GREY => "\x0315", |
56
|
2
|
|
|
2
|
|
13
|
}; |
|
2
|
|
|
|
|
4
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# list originally snatched from AnyEvent::IRC::Util |
59
|
|
|
|
|
|
|
our %NUMERIC2NAME = ( |
60
|
|
|
|
|
|
|
'001' => 'RPL_WELCOME', # RFC2812 |
61
|
|
|
|
|
|
|
'002' => 'RPL_YOURHOST', # RFC2812 |
62
|
|
|
|
|
|
|
'003' => 'RPL_CREATED', # RFC2812 |
63
|
|
|
|
|
|
|
'004' => 'RPL_MYINFO', # RFC2812 |
64
|
|
|
|
|
|
|
'005' => 'RPL_ISUPPORT', # draft-brocklesby-irc-isupport-03 |
65
|
|
|
|
|
|
|
'008' => 'RPL_SNOMASK', # Undernet |
66
|
|
|
|
|
|
|
'009' => 'RPL_STATMEMTOT', # Undernet |
67
|
|
|
|
|
|
|
'010' => 'RPL_STATMEM', # Undernet |
68
|
|
|
|
|
|
|
'020' => 'RPL_CONNECTING', # IRCnet |
69
|
|
|
|
|
|
|
'014' => 'RPL_YOURCOOKIE', # IRCnet |
70
|
|
|
|
|
|
|
'042' => 'RPL_YOURID', # IRCnet |
71
|
|
|
|
|
|
|
'043' => 'RPL_SAVENICK', # IRCnet |
72
|
|
|
|
|
|
|
'050' => 'RPL_ATTEMPTINGJUNC', # aircd |
73
|
|
|
|
|
|
|
'051' => 'RPL_ATTEMPTINGREROUTE', # aircd |
74
|
|
|
|
|
|
|
'200' => 'RPL_TRACELINK', # RFC1459 |
75
|
|
|
|
|
|
|
'201' => 'RPL_TRACECONNECTING', # RFC1459 |
76
|
|
|
|
|
|
|
'202' => 'RPL_TRACEHANDSHAKE', # RFC1459 |
77
|
|
|
|
|
|
|
'203' => 'RPL_TRACEUNKNOWN', # RFC1459 |
78
|
|
|
|
|
|
|
'204' => 'RPL_TRACEOPERATOR', # RFC1459 |
79
|
|
|
|
|
|
|
'205' => 'RPL_TRACEUSER', # RFC1459 |
80
|
|
|
|
|
|
|
'206' => 'RPL_TRACESERVER', # RFC1459 |
81
|
|
|
|
|
|
|
'207' => 'RPL_TRACESERVICE', # RFC2812 |
82
|
|
|
|
|
|
|
'208' => 'RPL_TRACENEWTYPE', # RFC1459 |
83
|
|
|
|
|
|
|
'209' => 'RPL_TRACECLASS', # RFC2812 |
84
|
|
|
|
|
|
|
'210' => 'RPL_STATS', # aircd |
85
|
|
|
|
|
|
|
'211' => 'RPL_STATSLINKINFO', # RFC1459 |
86
|
|
|
|
|
|
|
'212' => 'RPL_STATSCOMMANDS', # RFC1459 |
87
|
|
|
|
|
|
|
'213' => 'RPL_STATSCLINE', # RFC1459 |
88
|
|
|
|
|
|
|
'214' => 'RPL_STATSNLINE', # RFC1459 |
89
|
|
|
|
|
|
|
'215' => 'RPL_STATSILINE', # RFC1459 |
90
|
|
|
|
|
|
|
'216' => 'RPL_STATSKLINE', # RFC1459 |
91
|
|
|
|
|
|
|
'217' => 'RPL_STATSQLINE', # RFC1459 |
92
|
|
|
|
|
|
|
'218' => 'RPL_STATSYLINE', # RFC1459 |
93
|
|
|
|
|
|
|
'219' => 'RPL_ENDOFSTATS', # RFC1459 |
94
|
|
|
|
|
|
|
'221' => 'RPL_UMODEIS', # RFC1459 |
95
|
|
|
|
|
|
|
'231' => 'RPL_SERVICEINFO', # RFC1459 |
96
|
|
|
|
|
|
|
'233' => 'RPL_SERVICE', # RFC1459 |
97
|
|
|
|
|
|
|
'234' => 'RPL_SERVLIST', # RFC1459 |
98
|
|
|
|
|
|
|
'235' => 'RPL_SERVLISTEND', # RFC1459 |
99
|
|
|
|
|
|
|
'239' => 'RPL_STATSIAUTH', # IRCnet |
100
|
|
|
|
|
|
|
'241' => 'RPL_STATSLLINE', # RFC1459 |
101
|
|
|
|
|
|
|
'242' => 'RPL_STATSUPTIME', # RFC1459 |
102
|
|
|
|
|
|
|
'243' => 'RPL_STATSOLINE', # RFC1459 |
103
|
|
|
|
|
|
|
'244' => 'RPL_STATSHLINE', # RFC1459 |
104
|
|
|
|
|
|
|
'245' => 'RPL_STATSSLINE', # Bahamut, IRCnet, Hybrid |
105
|
|
|
|
|
|
|
'250' => 'RPL_STATSCONN', # ircu, Unreal |
106
|
|
|
|
|
|
|
'251' => 'RPL_LUSERCLIENT', # RFC1459 |
107
|
|
|
|
|
|
|
'252' => 'RPL_LUSEROP', # RFC1459 |
108
|
|
|
|
|
|
|
'253' => 'RPL_LUSERUNKNOWN', # RFC1459 |
109
|
|
|
|
|
|
|
'254' => 'RPL_LUSERCHANNELS', # RFC1459 |
110
|
|
|
|
|
|
|
'255' => 'RPL_LUSERME', # RFC1459 |
111
|
|
|
|
|
|
|
'256' => 'RPL_ADMINME', # RFC1459 |
112
|
|
|
|
|
|
|
'257' => 'RPL_ADMINLOC1', # RFC1459 |
113
|
|
|
|
|
|
|
'258' => 'RPL_ADMINLOC2', # RFC1459 |
114
|
|
|
|
|
|
|
'259' => 'RPL_ADMINEMAIL', # RFC1459 |
115
|
|
|
|
|
|
|
'261' => 'RPL_TRACELOG', # RFC1459 |
116
|
|
|
|
|
|
|
'262' => 'RPL_TRACEEND', # RFC2812 |
117
|
|
|
|
|
|
|
'263' => 'RPL_TRYAGAIN', # RFC2812 |
118
|
|
|
|
|
|
|
'265' => 'RPL_LOCALUSERS', # aircd, Bahamut, Hybrid |
119
|
|
|
|
|
|
|
'266' => 'RPL_GLOBALUSERS', # aircd, Bahamut, Hybrid |
120
|
|
|
|
|
|
|
'267' => 'RPL_START_NETSTAT', # aircd |
121
|
|
|
|
|
|
|
'268' => 'RPL_NETSTAT', # aircd |
122
|
|
|
|
|
|
|
'269' => 'RPL_END_NETSTAT', # aircd |
123
|
|
|
|
|
|
|
'270' => 'RPL_PRIVS', # ircu |
124
|
|
|
|
|
|
|
'271' => 'RPL_SILELIST', # ircu |
125
|
|
|
|
|
|
|
'272' => 'RPL_ENDOFSILELIST', # ircu |
126
|
|
|
|
|
|
|
'300' => 'RPL_NONE', # RFC1459 |
127
|
|
|
|
|
|
|
'301' => 'RPL_AWAY', # RFC1459 |
128
|
|
|
|
|
|
|
'302' => 'RPL_USERHOST', # RFC1459 |
129
|
|
|
|
|
|
|
'303' => 'RPL_ISON', # RFC1459 |
130
|
|
|
|
|
|
|
'305' => 'RPL_UNAWAY', # RFC1459 |
131
|
|
|
|
|
|
|
'306' => 'RPL_NOWAWAY', # RFC1459 |
132
|
|
|
|
|
|
|
'307' => 'RPL_WHOISREGNICK', # Bahamut, Unreal, Plexus |
133
|
|
|
|
|
|
|
'310' => 'RPL_WHOISMODES', # Plexus |
134
|
|
|
|
|
|
|
'311' => 'RPL_WHOISUSER', # RFC1459 |
135
|
|
|
|
|
|
|
'312' => 'RPL_WHOISSERVER', # RFC1459 |
136
|
|
|
|
|
|
|
'313' => 'RPL_WHOISOPERATOR', # RFC1459 |
137
|
|
|
|
|
|
|
'314' => 'RPL_WHOWASUSER', # RFC1459 |
138
|
|
|
|
|
|
|
'315' => 'RPL_ENDOFWHO', # RFC1459 |
139
|
|
|
|
|
|
|
'317' => 'RPL_WHOISIDLE', # RFC1459 |
140
|
|
|
|
|
|
|
'318' => 'RPL_ENDOFWHOIS', # RFC1459 |
141
|
|
|
|
|
|
|
'319' => 'RPL_WHOISCHANNELS', # RFC1459 |
142
|
|
|
|
|
|
|
'321' => 'RPL_LISTSTART', # RFC1459 |
143
|
|
|
|
|
|
|
'322' => 'RPL_LIST', # RFC1459 |
144
|
|
|
|
|
|
|
'323' => 'RPL_LISTEND', # RFC1459 |
145
|
|
|
|
|
|
|
'324' => 'RPL_CHANNELMODEIS', # RFC1459 |
146
|
|
|
|
|
|
|
'325' => 'RPL_UNIQOPIS', # RFC2812 |
147
|
|
|
|
|
|
|
'328' => 'RPL_CHANNEL_URL', # Bahamut, AustHex |
148
|
|
|
|
|
|
|
'329' => 'RPL_CREATIONTIME', # Bahamut |
149
|
|
|
|
|
|
|
'330' => 'RPL_WHOISACCOUNT', # ircu |
150
|
|
|
|
|
|
|
'331' => 'RPL_NOTOPIC', # RFC1459 |
151
|
|
|
|
|
|
|
'332' => 'RPL_TOPIC', # RFC1459 |
152
|
|
|
|
|
|
|
'333' => 'RPL_TOPICWHOTIME', # ircu |
153
|
|
|
|
|
|
|
'338' => 'RPL_WHOISACTUALLY', # Bahamut, ircu |
154
|
|
|
|
|
|
|
'340' => 'RPL_USERIP', # ircu |
155
|
|
|
|
|
|
|
'341' => 'RPL_INVITING', # RFC1459 |
156
|
|
|
|
|
|
|
'342' => 'RPL_SUMMONING', # RFC1459 |
157
|
|
|
|
|
|
|
'345' => 'RPL_INVITED', # GameSurge |
158
|
|
|
|
|
|
|
'346' => 'RPL_INVITELIST', # RFC2812 |
159
|
|
|
|
|
|
|
'347' => 'RPL_ENDOFINVITELIST', # RFC2812 |
160
|
|
|
|
|
|
|
'348' => 'RPL_EXCEPTLIST', # RFC2812 |
161
|
|
|
|
|
|
|
'349' => 'RPL_ENDOFEXCEPTLIST', # RFC2812 |
162
|
|
|
|
|
|
|
'351' => 'RPL_VERSION', # RFC1459 |
163
|
|
|
|
|
|
|
'352' => 'RPL_WHOREPLY', # RFC1459 |
164
|
|
|
|
|
|
|
'353' => 'RPL_NAMREPLY', # RFC1459 |
165
|
|
|
|
|
|
|
'354' => 'RPL_WHOSPCRPL', # ircu |
166
|
|
|
|
|
|
|
'355' => 'RPL_NAMREPLY_', # QuakeNet |
167
|
|
|
|
|
|
|
'361' => 'RPL_KILLDONE', # RFC1459 |
168
|
|
|
|
|
|
|
'362' => 'RPL_CLOSING', # RFC1459 |
169
|
|
|
|
|
|
|
'363' => 'RPL_CLOSEEND', # RFC1459 |
170
|
|
|
|
|
|
|
'364' => 'RPL_LINKS', # RFC1459 |
171
|
|
|
|
|
|
|
'365' => 'RPL_ENDOFLINKS', # RFC1459 |
172
|
|
|
|
|
|
|
'366' => 'RPL_ENDOFNAMES', # RFC1459 |
173
|
|
|
|
|
|
|
'367' => 'RPL_BANLIST', # RFC1459 |
174
|
|
|
|
|
|
|
'368' => 'RPL_ENDOFBANLIST', # RFC1459 |
175
|
|
|
|
|
|
|
'369' => 'RPL_ENDOFWHOWAS', # RFC1459 |
176
|
|
|
|
|
|
|
'371' => 'RPL_INFO', # RFC1459 |
177
|
|
|
|
|
|
|
'372' => 'RPL_MOTD', # RFC1459 |
178
|
|
|
|
|
|
|
'373' => 'RPL_INFOSTART', # RFC1459 |
179
|
|
|
|
|
|
|
'374' => 'RPL_ENDOFINFO', # RFC1459 |
180
|
|
|
|
|
|
|
'375' => 'RPL_MOTDSTART', # RFC1459 |
181
|
|
|
|
|
|
|
'376' => 'RPL_ENDOFMOTD', # RFC1459 |
182
|
|
|
|
|
|
|
'381' => 'RPL_YOUREOPER', # RFC1459 |
183
|
|
|
|
|
|
|
'382' => 'RPL_REHASHING', # RFC1459 |
184
|
|
|
|
|
|
|
'383' => 'RPL_YOURESERVICE', # RFC2812 |
185
|
|
|
|
|
|
|
'384' => 'RPL_MYPORTIS', # RFC1459 |
186
|
|
|
|
|
|
|
'385' => 'RPL_NOTOPERANYMORE', # AustHex, Hybrid, Unreal |
187
|
|
|
|
|
|
|
'391' => 'RPL_TIME', # RFC1459 |
188
|
|
|
|
|
|
|
'392' => 'RPL_USERSSTART', # RFC1459 |
189
|
|
|
|
|
|
|
'393' => 'RPL_USERS', # RFC1459 |
190
|
|
|
|
|
|
|
'394' => 'RPL_ENDOFUSERS', # RFC1459 |
191
|
|
|
|
|
|
|
'395' => 'RPL_NOUSERS', # RFC1459 |
192
|
|
|
|
|
|
|
'396' => 'RPL_HOSTHIDDEN', # Undernet |
193
|
|
|
|
|
|
|
'401' => 'ERR_NOSUCHNICK', # RFC1459 |
194
|
|
|
|
|
|
|
'402' => 'ERR_NOSUCHSERVER', # RFC1459 |
195
|
|
|
|
|
|
|
'403' => 'ERR_NOSUCHCHANNEL', # RFC1459 |
196
|
|
|
|
|
|
|
'404' => 'ERR_CANNOTSENDTOCHAN', # RFC1459 |
197
|
|
|
|
|
|
|
'405' => 'ERR_TOOMANYCHANNELS', # RFC1459 |
198
|
|
|
|
|
|
|
'406' => 'ERR_WASNOSUCHNICK', # RFC1459 |
199
|
|
|
|
|
|
|
'407' => 'ERR_TOOMANYTARGETS', # RFC1459 |
200
|
|
|
|
|
|
|
'408' => 'ERR_NOSUCHSERVICE', # RFC2812 |
201
|
|
|
|
|
|
|
'409' => 'ERR_NOORIGIN', # RFC1459 |
202
|
|
|
|
|
|
|
'411' => 'ERR_NORECIPIENT', # RFC1459 |
203
|
|
|
|
|
|
|
'412' => 'ERR_NOTEXTTOSEND', # RFC1459 |
204
|
|
|
|
|
|
|
'413' => 'ERR_NOTOPLEVEL', # RFC1459 |
205
|
|
|
|
|
|
|
'414' => 'ERR_WILDTOPLEVEL', # RFC1459 |
206
|
|
|
|
|
|
|
'415' => 'ERR_BADMASK', # RFC2812 |
207
|
|
|
|
|
|
|
'421' => 'ERR_UNKNOWNCOMMAND', # RFC1459 |
208
|
|
|
|
|
|
|
'422' => 'ERR_NOMOTD', # RFC1459 |
209
|
|
|
|
|
|
|
'423' => 'ERR_NOADMININFO', # RFC1459 |
210
|
|
|
|
|
|
|
'424' => 'ERR_FILEERROR', # RFC1459 |
211
|
|
|
|
|
|
|
'425' => 'ERR_NOOPERMOTD', # Unreal |
212
|
|
|
|
|
|
|
'429' => 'ERR_TOOMANYAWAY', # Bahamut |
213
|
|
|
|
|
|
|
'430' => 'ERR_EVENTNICKCHANGE', # AustHex |
214
|
|
|
|
|
|
|
'431' => 'ERR_NONICKNAMEGIVEN', # RFC1459 |
215
|
|
|
|
|
|
|
'432' => 'ERR_ERRONEUSNICKNAME', # RFC1459 |
216
|
|
|
|
|
|
|
'433' => 'ERR_NICKNAMEINUSE', # RFC1459 |
217
|
|
|
|
|
|
|
'436' => 'ERR_NICKCOLLISION', # RFC1459 |
218
|
|
|
|
|
|
|
'439' => 'ERR_TARGETTOOFAST', # ircu |
219
|
|
|
|
|
|
|
'440' => 'ERR_SERCVICESDOWN', # Bahamut, Unreal |
220
|
|
|
|
|
|
|
'441' => 'ERR_USERNOTINCHANNEL', # RFC1459 |
221
|
|
|
|
|
|
|
'442' => 'ERR_NOTONCHANNEL', # RFC1459 |
222
|
|
|
|
|
|
|
'443' => 'ERR_USERONCHANNEL', # RFC1459 |
223
|
|
|
|
|
|
|
'444' => 'ERR_NOLOGIN', # RFC1459 |
224
|
|
|
|
|
|
|
'445' => 'ERR_SUMMONDISABLED', # RFC1459 |
225
|
|
|
|
|
|
|
'446' => 'ERR_USERSDISABLED', # RFC1459 |
226
|
|
|
|
|
|
|
'447' => 'ERR_NONICKCHANGE', # Unreal |
227
|
|
|
|
|
|
|
'449' => 'ERR_NOTIMPLEMENTED', # Undernet |
228
|
|
|
|
|
|
|
'451' => 'ERR_NOTREGISTERED', # RFC1459 |
229
|
|
|
|
|
|
|
'455' => 'ERR_HOSTILENAME', # Unreal |
230
|
|
|
|
|
|
|
'459' => 'ERR_NOHIDING', # Unreal |
231
|
|
|
|
|
|
|
'460' => 'ERR_NOTFORHALFOPS', # Unreal |
232
|
|
|
|
|
|
|
'461' => 'ERR_NEEDMOREPARAMS', # RFC1459 |
233
|
|
|
|
|
|
|
'462' => 'ERR_ALREADYREGISTRED', # RFC1459 |
234
|
|
|
|
|
|
|
'463' => 'ERR_NOPERMFORHOST', # RFC1459 |
235
|
|
|
|
|
|
|
'464' => 'ERR_PASSWDMISMATCH', # RFC1459 |
236
|
|
|
|
|
|
|
'465' => 'ERR_YOUREBANNEDCREEP', # RFC1459 |
237
|
|
|
|
|
|
|
'466' => 'ERR_YOUWILLBEBANNED', # RFC1459 |
238
|
|
|
|
|
|
|
'467' => 'ERR_KEYSET', # RFC1459 |
239
|
|
|
|
|
|
|
'469' => 'ERR_LINKSET', # Unreal |
240
|
|
|
|
|
|
|
'471' => 'ERR_CHANNELISFULL', # RFC1459 |
241
|
|
|
|
|
|
|
'472' => 'ERR_UNKNOWNMODE', # RFC1459 |
242
|
|
|
|
|
|
|
'473' => 'ERR_INVITEONLYCHAN', # RFC1459 |
243
|
|
|
|
|
|
|
'474' => 'ERR_BANNEDFROMCHAN', # RFC1459 |
244
|
|
|
|
|
|
|
'475' => 'ERR_BADCHANNELKEY', # RFC1459 |
245
|
|
|
|
|
|
|
'476' => 'ERR_BADCHANMASK', # RFC2812 |
246
|
|
|
|
|
|
|
'477' => 'ERR_NOCHANMODES', # RFC2812 |
247
|
|
|
|
|
|
|
'478' => 'ERR_BANLISTFULL', # RFC2812 |
248
|
|
|
|
|
|
|
'481' => 'ERR_NOPRIVILEGES', # RFC1459 |
249
|
|
|
|
|
|
|
'482' => 'ERR_CHANOPRIVSNEEDED', # RFC1459 |
250
|
|
|
|
|
|
|
'483' => 'ERR_CANTKILLSERVER', # RFC1459 |
251
|
|
|
|
|
|
|
'484' => 'ERR_RESTRICTED', # RFC2812 |
252
|
|
|
|
|
|
|
'485' => 'ERR_UNIQOPPRIVSNEEDED', # RFC2812 |
253
|
|
|
|
|
|
|
'488' => 'ERR_TSLESSCHAN', # IRCnet |
254
|
|
|
|
|
|
|
'491' => 'ERR_NOOPERHOST', # RFC1459 |
255
|
|
|
|
|
|
|
'492' => 'ERR_NOSERVICEHOST', # RFC1459 |
256
|
|
|
|
|
|
|
'493' => 'ERR_NOFEATURE', # ircu |
257
|
|
|
|
|
|
|
'494' => 'ERR_BADFEATURE', # ircu |
258
|
|
|
|
|
|
|
'495' => 'ERR_BADLOGTYPE', # ircu |
259
|
|
|
|
|
|
|
'496' => 'ERR_BADLOGSYS', # ircu |
260
|
|
|
|
|
|
|
'497' => 'ERR_BADLOGVALUE', # ircu |
261
|
|
|
|
|
|
|
'498' => 'ERR_ISOPERLCHAN', # ircu |
262
|
|
|
|
|
|
|
'501' => 'ERR_UMODEUNKNOWNFLAG', # RFC1459 |
263
|
|
|
|
|
|
|
'502' => 'ERR_USERSDONTMATCH', # RFC1459 |
264
|
|
|
|
|
|
|
'503' => 'ERR_GHOSTEDCLIENT', # Hybrid |
265
|
|
|
|
|
|
|
); |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
our %NAME2NUMERIC; |
268
|
|
|
|
|
|
|
while (my ($key, $val) = each %NUMERIC2NAME) { |
269
|
|
|
|
|
|
|
$NAME2NUMERIC{$val} = $key; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub numeric_to_name { |
273
|
1
|
|
|
1
|
1
|
2315
|
my ($code) = @_; |
274
|
1
|
|
|
|
|
13
|
return $NUMERIC2NAME{$code}; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub name_to_numeric { |
278
|
1
|
|
|
1
|
1
|
3
|
my ($name) = @_; |
279
|
1
|
|
|
|
|
7
|
return $NAME2NUMERIC{$name}; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub uc_irc { |
283
|
12
|
|
|
12
|
1
|
31
|
my ($value, $type) = @_; |
284
|
12
|
50
|
|
|
|
24
|
return if !defined $value; |
285
|
12
|
100
|
|
|
|
29
|
$type = 'rfc1459' if !defined $type; |
286
|
12
|
|
|
|
|
22
|
$type = lc $type; |
287
|
|
|
|
|
|
|
|
288
|
12
|
100
|
|
|
|
36
|
if ($type eq 'ascii') { |
|
|
100
|
|
|
|
|
|
289
|
1
|
|
|
|
|
3
|
$value =~ tr/a-z/A-Z/; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
elsif ($type eq 'strict-rfc1459') { |
292
|
1
|
|
|
|
|
3
|
$value =~ tr/a-z{}|/A-Z[]\\/; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
else { |
295
|
10
|
|
|
|
|
18
|
$value =~ tr/a-z{}|^/A-Z[]\\~/; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
12
|
|
|
|
|
47
|
return $value; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub lc_irc { |
302
|
6
|
|
|
6
|
1
|
14
|
my ($value, $type) = @_; |
303
|
6
|
50
|
|
|
|
17
|
return if !defined $value; |
304
|
6
|
100
|
|
|
|
15
|
$type = 'rfc1459' if !defined $type; |
305
|
6
|
|
|
|
|
11
|
$type = lc $type; |
306
|
|
|
|
|
|
|
|
307
|
6
|
100
|
|
|
|
21
|
if ($type eq 'ascii') { |
|
|
100
|
|
|
|
|
|
308
|
1
|
|
|
|
|
5
|
$value =~ tr/A-Z/a-z/; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
elsif ($type eq 'strict-rfc1459') { |
311
|
1
|
|
|
|
|
3
|
$value =~ tr/A-Z[]\\/a-z{}|/; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
else { |
314
|
4
|
|
|
|
|
8
|
$value =~ tr/A-Z[]\\~/a-z{}|^/; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
6
|
|
|
|
|
31
|
return $value; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub eq_irc { |
321
|
1
|
|
|
1
|
1
|
3
|
my ($first, $second, $type) = @_; |
322
|
1
|
50
|
33
|
|
|
8
|
return if !defined $first || !defined $second; |
323
|
1
|
50
|
|
|
|
3
|
return 1 if lc_irc($first, $type) eq lc_irc($second, $type); |
324
|
0
|
|
|
|
|
0
|
return; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub parse_mode_line { |
328
|
3
|
|
|
3
|
1
|
2250
|
my @args = @_; |
329
|
|
|
|
|
|
|
|
330
|
3
|
|
|
|
|
10
|
my $chanmodes = [qw(beI k l imnpstaqr)]; |
331
|
3
|
|
|
|
|
6
|
my $statmodes = 'ohv'; |
332
|
3
|
|
|
|
|
7
|
my $hashref = { }; |
333
|
3
|
|
|
|
|
5
|
my $count = 0; |
334
|
|
|
|
|
|
|
|
335
|
3
|
|
|
|
|
12
|
while (my $arg = shift @args) { |
336
|
3
|
50
|
66
|
|
|
35
|
if ( ref $arg eq 'ARRAY' ) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
337
|
0
|
|
|
|
|
0
|
$chanmodes = $arg; |
338
|
0
|
|
|
|
|
0
|
next; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
elsif (ref $arg eq 'HASH') { |
341
|
0
|
|
|
|
|
0
|
$statmodes = join '', keys %{ $arg }; |
|
0
|
|
|
|
|
0
|
|
342
|
0
|
|
|
|
|
0
|
next; |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
elsif ($arg =~ /^[-+]/ or $count == 0) { |
345
|
3
|
|
|
|
|
5
|
my $action = '+'; |
346
|
3
|
|
|
|
|
11
|
for my $char (split //, $arg) { |
347
|
6
|
100
|
100
|
|
|
27
|
if ($char eq '+' or $char eq '-') { |
348
|
2
|
|
|
|
|
4
|
$action = $char; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
else { |
351
|
4
|
|
|
|
|
6
|
push @{ $hashref->{modes} }, $action . $char; |
|
4
|
|
|
|
|
16
|
|
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
6
|
100
|
33
|
|
|
97
|
if (length $chanmodes->[0] && length $chanmodes->[1] && length $statmodes |
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
355
|
|
|
|
|
|
|
&& $char =~ /[$statmodes$chanmodes->[0]$chanmodes->[1]]/) { |
356
|
4
|
|
|
|
|
14
|
push @{ $hashref->{args} }, shift @args; |
|
4
|
|
|
|
|
13
|
|
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
6
|
50
|
66
|
|
|
74
|
if (length $chanmodes->[2] && $action eq '+' && $char =~ /[$chanmodes->[2]]/) { |
|
|
|
66
|
|
|
|
|
360
|
0
|
|
|
|
|
0
|
push @{ $hashref->{args} }, shift @args; |
|
0
|
|
|
|
|
0
|
|
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
else { |
365
|
0
|
|
|
|
|
0
|
push @{ $hashref->{args} }, $arg; |
|
0
|
|
|
|
|
0
|
|
366
|
|
|
|
|
|
|
} |
367
|
3
|
|
|
|
|
11
|
$count++; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
3
|
|
|
|
|
12
|
return $hashref; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub normalize_mask { |
374
|
2
|
|
|
2
|
1
|
1247
|
my ($arg) = @_; |
375
|
2
|
50
|
|
|
|
6
|
return if !defined $arg; |
376
|
|
|
|
|
|
|
|
377
|
2
|
|
|
|
|
5
|
$arg =~ s/\*{2,}/*/g; |
378
|
2
|
|
|
|
|
3
|
my @mask; |
379
|
|
|
|
|
|
|
my $remainder; |
380
|
2
|
100
|
66
|
|
|
13
|
if ($arg !~ /!/ and $arg =~ /@/) { |
381
|
1
|
|
|
|
|
2
|
$remainder = $arg; |
382
|
1
|
|
|
|
|
2
|
$mask[0] = '*'; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
else { |
385
|
1
|
|
|
|
|
5
|
($mask[0], $remainder) = split /!/, $arg, 2; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
2
|
100
|
|
|
|
8
|
$remainder =~ s/!//g if defined $remainder; |
389
|
2
|
100
|
|
|
|
7
|
@mask[1..2] = split(/@/, $remainder, 2) if defined $remainder; |
390
|
2
|
100
|
|
|
|
7
|
$mask[2] =~ s/@//g if defined $mask[2]; |
391
|
|
|
|
|
|
|
|
392
|
2
|
|
|
|
|
5
|
for my $i (1..2) { |
393
|
4
|
100
|
|
|
|
13
|
$mask[$i] = '*' if !defined $mask[$i]; |
394
|
|
|
|
|
|
|
} |
395
|
2
|
|
|
|
|
9
|
return $mask[0] . '!' . $mask[1] . '@' . $mask[2]; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub unparse_mode_line { |
399
|
4
|
|
|
4
|
1
|
8
|
my ($line) = @_; |
400
|
4
|
50
|
33
|
|
|
26
|
return if !defined $line || !length $line; |
401
|
|
|
|
|
|
|
|
402
|
4
|
|
|
|
|
8
|
my $action; my $return; |
403
|
4
|
|
|
|
|
17
|
for my $mode ( split(//,$line) ) { |
404
|
48
|
100
|
100
|
|
|
310
|
if ($mode =~ /^(\+|-)$/ && (!$action || $mode ne $action)) { |
|
|
|
66
|
|
|
|
|
405
|
9
|
|
|
|
|
12
|
$return .= $mode; |
406
|
9
|
|
|
|
|
12
|
$action = $mode; |
407
|
9
|
|
|
|
|
13
|
next; |
408
|
|
|
|
|
|
|
} |
409
|
39
|
100
|
100
|
|
|
155
|
$return .= $mode if ($mode ne '+' and $mode ne '-'); |
410
|
|
|
|
|
|
|
} |
411
|
4
|
|
|
|
|
18
|
$return =~ s/[+-]$//; |
412
|
4
|
|
|
|
|
32
|
return $return; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
sub gen_mode_change { |
416
|
3
|
|
|
3
|
1
|
9
|
my ($before, $after) = @_; |
417
|
3
|
50
|
|
|
|
10
|
$before = '' if !defined $before; |
418
|
3
|
50
|
|
|
|
8
|
$after = '' if !defined $after; |
419
|
|
|
|
|
|
|
|
420
|
3
|
|
|
|
|
12
|
my @before = split //, $before; |
421
|
3
|
|
|
|
|
11
|
my @after = split //, $after; |
422
|
3
|
|
|
|
|
6
|
my $string = ''; |
423
|
3
|
|
|
|
|
9
|
my @hunks = _diff(\@before, \@after); |
424
|
3
|
|
|
|
|
20
|
$string .= $_->[0] . $_->[1] for @hunks; |
425
|
|
|
|
|
|
|
|
426
|
3
|
|
|
|
|
8
|
return unparse_mode_line($string); |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub is_valid_nick_name { |
430
|
2
|
|
|
2
|
1
|
25
|
my ($nickname) = @_; |
431
|
2
|
50
|
33
|
|
|
12
|
return if !defined $nickname || !length $nickname; |
432
|
2
|
100
|
|
|
|
14
|
return 1 if $nickname =~ /^[A-Za-z_`\-^\|\\\{}\[\]][A-Za-z_0-9`\-^\|\\\{}\[\]]*$/; |
433
|
1
|
|
|
|
|
4
|
return; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub is_valid_chan_name { |
437
|
3
|
|
|
3
|
1
|
7
|
my $channel = shift; |
438
|
3
|
|
50
|
|
|
21
|
my $chantypes = shift || ['#', '&']; |
439
|
3
|
50
|
|
|
|
10
|
return if !@$chantypes; |
440
|
3
|
|
|
|
|
7
|
my $chanprefix = join '', @$chantypes; |
441
|
3
|
50
|
33
|
|
|
25
|
return if !defined $channel || !length $channel; |
442
|
|
|
|
|
|
|
|
443
|
3
|
100
|
|
|
|
14
|
return if bytes::length($channel) > 200; |
444
|
2
|
100
|
|
|
|
7164
|
return 1 if $channel =~ /^[$chanprefix][^ \a\0\012\015,:]+$/; |
445
|
1
|
|
|
|
|
5
|
return; |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
sub matches_mask_array { |
449
|
2
|
|
|
2
|
1
|
4
|
my ($masks, $matches, $mapping) = @_; |
450
|
|
|
|
|
|
|
|
451
|
2
|
50
|
33
|
|
|
9
|
return if !defined $masks || !defined $matches; |
452
|
2
|
50
|
|
|
|
7
|
return if ref $masks ne 'ARRAY'; |
453
|
2
|
50
|
|
|
|
4
|
return if ref $matches ne 'ARRAY'; |
454
|
2
|
|
|
|
|
3
|
my $ref = { }; |
455
|
|
|
|
|
|
|
|
456
|
2
|
|
|
|
|
4
|
for my $mask (@$masks) { |
457
|
2
|
|
|
|
|
3
|
for my $match (@$matches) { |
458
|
2
|
100
|
|
|
|
3
|
if (matches_mask($mask, $match, $mapping)) { |
459
|
1
|
|
|
|
|
3
|
push @{ $ref->{ $mask } }, $match; |
|
1
|
|
|
|
|
12
|
|
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
2
|
|
|
|
|
10
|
return $ref; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
sub matches_mask { |
468
|
4
|
|
|
4
|
1
|
404
|
my ($mask, $match, $mapping) = @_; |
469
|
4
|
50
|
33
|
|
|
15
|
return if !defined $mask || !length $mask; |
470
|
4
|
50
|
33
|
|
|
15
|
return if !defined $match || !length $match; |
471
|
|
|
|
|
|
|
|
472
|
4
|
|
|
|
|
8
|
my $umask = quotemeta uc_irc($mask, $mapping); |
473
|
4
|
|
|
|
|
14
|
$umask =~ s/\\\*/[\x01-\xFF]{0,}/g; |
474
|
4
|
|
|
|
|
6
|
$umask =~ s/\\\?/[\x01-\xFF]{1,1}/g; |
475
|
4
|
|
|
|
|
8
|
$match = uc_irc($match, $mapping); |
476
|
|
|
|
|
|
|
|
477
|
4
|
100
|
|
|
|
61
|
return 1 if $match =~ /^$umask$/; |
478
|
2
|
|
|
|
|
8
|
return; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub parse_user { |
482
|
2
|
|
|
2
|
1
|
12
|
my ($user) = @_; |
483
|
2
|
50
|
|
|
|
6
|
return if !defined $user; |
484
|
|
|
|
|
|
|
|
485
|
2
|
|
|
|
|
11
|
my ($n, $u, $h) = split /[!@]/, $user; |
486
|
2
|
100
|
|
|
|
8
|
return ($n, $u, $h) if wantarray(); |
487
|
1
|
|
|
|
|
4
|
return $n; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
sub has_color { |
491
|
3
|
|
|
3
|
1
|
1713
|
my ($string) = @_; |
492
|
3
|
50
|
|
|
|
10
|
return if !defined $string; |
493
|
3
|
100
|
|
|
|
18
|
return 1 if $string =~ /[\x03\x04\x1B]/; |
494
|
1
|
|
|
|
|
7
|
return; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub has_formatting { |
498
|
5
|
|
|
5
|
1
|
7
|
my ($string) = @_; |
499
|
5
|
50
|
|
|
|
13
|
return if !defined $string; |
500
|
5
|
100
|
|
|
|
24
|
return 1 if $string =~/[\x02\x1f\x16\x1d\x11\x06]/; |
501
|
3
|
|
|
|
|
10
|
return; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
sub strip_color { |
505
|
4
|
|
|
4
|
1
|
643
|
my ($string) = @_; |
506
|
4
|
50
|
|
|
|
13
|
return if !defined $string; |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# mIRC colors |
509
|
4
|
|
|
|
|
32
|
$string =~ s/\x03(?:,\d{1,2}|\d{1,2}(?:,\d{1,2})?)?//g; |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# RGB colors supported by some clients |
512
|
4
|
|
|
|
|
49
|
$string =~ s/\x04[0-9a-fA-F]{0,6}//ig; |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
# see ECMA-48 + advice by urxvt author |
515
|
4
|
|
|
|
|
8
|
$string =~ s/\x1B\[.*?[\x00-\x1F\x40-\x7E]//g; |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# strip cancellation codes too if there are no formatting codes |
518
|
4
|
100
|
|
|
|
11
|
$string =~ s/\x0f//g if !has_formatting($string); |
519
|
4
|
|
|
|
|
16
|
return $string; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub strip_formatting { |
523
|
2
|
|
|
2
|
1
|
7
|
my ($string) = @_; |
524
|
2
|
50
|
|
|
|
8
|
return if !defined $string; |
525
|
2
|
|
|
|
|
18
|
$string =~ s/[\x02\x1f\x16\x1d\x11\x06]//g; |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# strip cancellation codes too if there are no color codes |
528
|
2
|
100
|
|
|
|
8
|
$string =~ s/\x0f//g if !has_color($string); |
529
|
|
|
|
|
|
|
|
530
|
2
|
|
|
|
|
8
|
return $string; |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub decode_irc { |
534
|
2
|
|
|
2
|
1
|
29848
|
my ($line) = @_; |
535
|
2
|
|
|
|
|
18
|
my $utf8 = guess_encoding($line, 'utf8'); |
536
|
2
|
100
|
|
|
|
532
|
return ref $utf8 ? decode('utf8', $line) : decode('cp1252', $line); |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub _diff { |
540
|
3
|
|
|
3
|
|
6
|
my ($before, $after) = @_; |
541
|
3
|
|
|
|
|
6
|
my %in_before; |
542
|
3
|
|
|
|
|
12
|
@in_before{@$before} = (); |
543
|
3
|
|
|
|
|
11
|
my %in_after; |
544
|
3
|
|
|
|
|
10
|
@in_after{@$after} = (); |
545
|
3
|
|
|
|
|
4
|
my (@diff, %seen); |
546
|
|
|
|
|
|
|
|
547
|
3
|
|
|
|
|
5
|
for my $seen (@$before) { |
548
|
8
|
100
|
66
|
|
|
40
|
next if exists $seen{$seen} || exists $in_after{$seen}; |
549
|
6
|
|
|
|
|
8
|
$seen{$seen} = 1; |
550
|
6
|
|
|
|
|
17
|
push @diff, ['-', $seen]; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
3
|
|
|
|
|
7
|
%seen = (); |
554
|
|
|
|
|
|
|
|
555
|
3
|
|
|
|
|
6
|
for my $seen (@$after) { |
556
|
12
|
100
|
66
|
|
|
59
|
next if exists $seen{$seen} || exists $in_before{$seen}; |
557
|
10
|
|
|
|
|
15
|
$seen{$seen} = 1; |
558
|
10
|
|
|
|
|
28
|
push @diff, ['+', $seen]; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
3
|
|
|
|
|
18
|
return @diff; |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
1; |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=encoding utf8 |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head1 NAME |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
IRC::Utils - Common utilities for IRC-related tasks |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
=head1 SYNOPSIS |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
use strict; |
575
|
|
|
|
|
|
|
use warnings; |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
use IRC::Utils ':ALL'; |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
my $nickname = '^Lame|BOT[moo]'; |
580
|
|
|
|
|
|
|
my $uppercase_nick = uc_irc($nickname); |
581
|
|
|
|
|
|
|
my $lowercase_nick = lc_irc($nickname); |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
print "They're equivalent\n" if eq_irc($uppercase_nick, $lowercase_nick); |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
my $mode_line = 'ov+b-i Bob sue stalin*!*@*'; |
586
|
|
|
|
|
|
|
my $hashref = parse_mode_line($mode_line); |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
my $banmask = 'stalin*'; |
589
|
|
|
|
|
|
|
my $full_banmask = normalize_mask($banmask); |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
if (matches_mask($full_banmask, 'stalin!joe@kremlin.ru')) { |
592
|
|
|
|
|
|
|
print "EEK!"; |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
my $decoded = irc_decode($raw_irc_message); |
596
|
|
|
|
|
|
|
print $decoded, "\n"; |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
if (has_color($message)) { |
599
|
|
|
|
|
|
|
print 'COLOR CODE ALERT!\n"; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my $results_hashref = matches_mask_array(\@masks, \@items_to_match_against); |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
my $nick = parse_user('stalin!joe@kremlin.ru'); |
605
|
|
|
|
|
|
|
my ($nick, $user, $host) = parse_user('stalin!joe@kremlin.ru'); |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
=head1 DESCRIPTION |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
The functions in this module take care of many of the tasks you are faced |
610
|
|
|
|
|
|
|
with when working with IRC. Mode lines, ban masks, message encoding and |
611
|
|
|
|
|
|
|
formatting, etc. |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=head1 FUNCTIONS |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=head2 C |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
Takes one mandatory parameter, a string to convert to IRC uppercase, and one |
618
|
|
|
|
|
|
|
optional parameter, the casemapping of the ircd (which can be B<'rfc1459'>, |
619
|
|
|
|
|
|
|
B<'strict-rfc1459'> or B<'ascii'>. Default is B<'rfc1459'>). Returns the IRC |
620
|
|
|
|
|
|
|
uppercase equivalent of the passed string. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 C |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Takes one mandatory parameter, a string to convert to IRC lowercase, and one |
625
|
|
|
|
|
|
|
optional parameter, the casemapping of the ircd (which can be B<'rfc1459'>, |
626
|
|
|
|
|
|
|
B<'strict-rfc1459'> or B<'ascii'>. Default is B<'rfc1459'>). Returns the IRC |
627
|
|
|
|
|
|
|
lowercase equivalent of the passed string. |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head2 C |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
Takes two mandatory parameters, IRC strings (channels or nicknames) to |
632
|
|
|
|
|
|
|
compare. A third, optional parameter specifies the casemapping. Returns true |
633
|
|
|
|
|
|
|
if the two strings are equivalent, false otherwise |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# long version |
636
|
|
|
|
|
|
|
lc_irc($one, $map) eq lc_irc($two, $map) |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# short version |
639
|
|
|
|
|
|
|
eq_irc($one, $two, $map) |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=head2 C |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
Takes a list representing an IRC mode line. Returns a hashref. Optionally |
644
|
|
|
|
|
|
|
you can also supply an arrayref and a hashref to specify valid channel |
645
|
|
|
|
|
|
|
modes (default: C<[qw(beI k l imnpstaqr)]>) and status modes (default: |
646
|
|
|
|
|
|
|
C<< {o => '@', h => '%', v => '+'} >>), respectively. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
If the modeline |
649
|
|
|
|
|
|
|
couldn't be parsed the hashref will be empty. On success the following keys |
650
|
|
|
|
|
|
|
will be available in the hashref: |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
B<'modes'>, an arrayref of normalised modes; |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
B<'args'>, an arrayref of applicable arguments to the modes; |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Example: |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
my $hashref = parse_mode_line( 'ov+b-i', 'Bob', 'sue', 'stalin*!*@*' ); |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# $hashref will be: |
661
|
|
|
|
|
|
|
{ |
662
|
|
|
|
|
|
|
modes => [ '+o', '+v', '+b', '-i' ], |
663
|
|
|
|
|
|
|
args => [ 'Bob', 'sue', 'stalin*!*@*' ], |
664
|
|
|
|
|
|
|
} |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=head2 C |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
Takes one parameter, a string representing an IRC mask. Returns a normalised |
669
|
|
|
|
|
|
|
full mask. |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
Example: |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
$fullbanmask = normalize_mask( 'stalin*' ); |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# $fullbanmask will be: 'stalin*!*@*'; |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head2 C |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Takes two parameters, a string representing an IRC mask and something to |
680
|
|
|
|
|
|
|
match against the IRC mask, such as a nick!user@hostname string. Returns |
681
|
|
|
|
|
|
|
a true value if they match, a false value otherwise. Optionally, one may |
682
|
|
|
|
|
|
|
pass the casemapping (see L|/uc_irc>), as this function uses |
683
|
|
|
|
|
|
|
C internally. |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head2 C |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Takes two array references, the first being a list of strings representing |
688
|
|
|
|
|
|
|
IRC masks, the second a list of somethings to test against the masks. Returns |
689
|
|
|
|
|
|
|
an empty hashref if there are no matches. Otherwise, the keys will be the |
690
|
|
|
|
|
|
|
masks matched, each value being an arrayref of the strings that matched it. |
691
|
|
|
|
|
|
|
Optionally, one may pass the casemapping (see L|/uc_irc>), as |
692
|
|
|
|
|
|
|
this function uses C internally. |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=head2 C |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
Takes one argument, a string representing a number of mode changes. Returns |
697
|
|
|
|
|
|
|
a condensed version of the changes. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
my $mode_line = unparse_mode_line('+o+o+o-v+v'); |
700
|
|
|
|
|
|
|
$mode_line is now '+ooo-v+v' |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=head2 C |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Takes two arguments, strings representing a set of IRC user modes before and |
705
|
|
|
|
|
|
|
after a change. Returns a string representing what changed. |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
my $mode_change = gen_mode_change('abcde', 'befmZ'); |
708
|
|
|
|
|
|
|
$mode_change is now '-acd+fmZ' |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
=head2 C |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
Takes one parameter, a string representing a user in the form |
713
|
|
|
|
|
|
|
nick!user@hostname. In a scalar context it returns just the nickname. |
714
|
|
|
|
|
|
|
In a list context it returns a list consisting of the nick, user and hostname, |
715
|
|
|
|
|
|
|
respectively. |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 C |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
Takes one argument, a channel name to validate. Returns true or false if the |
720
|
|
|
|
|
|
|
channel name is valid or not. You can supply a second argument, an array of |
721
|
|
|
|
|
|
|
characters of allowed channel prefixes. Defaults to C<['#', '&']>. |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=head2 C |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
Takes one argument, a nickname to validate. Returns true or false if the |
726
|
|
|
|
|
|
|
nickname is valid or not. |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=head2 C |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
Takes an IRC server numerical reply code (e.g. '001') as an argument, and |
731
|
|
|
|
|
|
|
returns the corresponding name (e.g. 'RPL_WELCOME'). |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=head2 C |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Takes an IRC server reply name (e.g. 'RPL_WELCOME') as an argument, and returns the |
736
|
|
|
|
|
|
|
corresponding numerical code (e.g. '001'). |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=head2 C |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns true if it contains any IRC |
741
|
|
|
|
|
|
|
color codes, false otherwise. Useful if you want your bot to kick users for |
742
|
|
|
|
|
|
|
(ab)using colors. :) |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
=head2 C |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns true if it contains any IRC |
747
|
|
|
|
|
|
|
formatting codes, false otherwise. |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=head2 C |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns the string stripped of all |
752
|
|
|
|
|
|
|
IRC color codes. |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=head2 C |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
Takes one parameter, a string of IRC text. Returns the string stripped of all |
757
|
|
|
|
|
|
|
IRC formatting codes. |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
=head2 C |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
This function takes a byte string (i.e. an unmodified IRC message) and |
762
|
|
|
|
|
|
|
returns a text string. Since the source encoding might have been UTF-8, |
763
|
|
|
|
|
|
|
you should store it with UTF-8 or some other Unicode encoding in your |
764
|
|
|
|
|
|
|
file/database/whatever to be safe. For a more detailed discussion, see |
765
|
|
|
|
|
|
|
L. |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
use IRC::Utils qw(decode_irc); |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
sub message_handler { |
770
|
|
|
|
|
|
|
my ($nick, $channel, $message) = @_; |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# not wise, $message is a byte string of unkown encoding |
773
|
|
|
|
|
|
|
print $message, "\n"; |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
$message = decode_irc($what); |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
# good, $message is a text string |
778
|
|
|
|
|
|
|
print $message, "\n"; |
779
|
|
|
|
|
|
|
} |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=head1 CONSTANTS |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
Use the following constants to add formatting and mIRC color codes to IRC |
784
|
|
|
|
|
|
|
messages. |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
Normal text: |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
NORMAL |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
Formatting: |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
BOLD |
793
|
|
|
|
|
|
|
UNDERLINE |
794
|
|
|
|
|
|
|
REVERSE |
795
|
|
|
|
|
|
|
ITALIC |
796
|
|
|
|
|
|
|
FIXED |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
Colors: |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
WHITE |
801
|
|
|
|
|
|
|
BLACK |
802
|
|
|
|
|
|
|
BLUE |
803
|
|
|
|
|
|
|
GREEN |
804
|
|
|
|
|
|
|
RED |
805
|
|
|
|
|
|
|
BROWN |
806
|
|
|
|
|
|
|
PURPLE |
807
|
|
|
|
|
|
|
ORANGE |
808
|
|
|
|
|
|
|
YELLOW |
809
|
|
|
|
|
|
|
LIGHT_GREEN |
810
|
|
|
|
|
|
|
TEAL |
811
|
|
|
|
|
|
|
LIGHT_CYAN |
812
|
|
|
|
|
|
|
LIGHT_BLUE |
813
|
|
|
|
|
|
|
PINK |
814
|
|
|
|
|
|
|
GREY |
815
|
|
|
|
|
|
|
LIGHT_GREY |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Individual non-color formatting codes can be cancelled with their |
818
|
|
|
|
|
|
|
corresponding constant, but you can also cancel all of them at once with |
819
|
|
|
|
|
|
|
C. To cancel the effect of color codes, you must use C. |
820
|
|
|
|
|
|
|
which of course has the side effect of cancelling all other formatting codes |
821
|
|
|
|
|
|
|
as well. |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
$msg = 'This word is '.YELLOW.'yellow'.NORMAL.' while this word is'.BOLD.'bold'.BOLD; |
824
|
|
|
|
|
|
|
$msg = UNDERLINE.BOLD.'This sentence is both underlined and bold.'.NORMAL; |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
=head1 ENCODING |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=head2 Messages |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
The only encoding requirement the IRC protocol places on its messages is |
831
|
|
|
|
|
|
|
that they be 8-bits and ASCII-compatible. This has resulted in most of the |
832
|
|
|
|
|
|
|
Western world settling on ASCII-compatible Latin-1 (usually Microsoft's |
833
|
|
|
|
|
|
|
CP1252, a Latin-1 variant) as a convention. Recently, popular IRC clients |
834
|
|
|
|
|
|
|
(mIRC, xchat, certain irssi configurations) have begun sending a mixture of |
835
|
|
|
|
|
|
|
CP1252 and UTF-8 over the wire to allow more characters without breaking |
836
|
|
|
|
|
|
|
backward compatibility (too much). They send CP1252 encoded messages if the |
837
|
|
|
|
|
|
|
characters fit within that encoding, otherwise falling back to UTF-8, and |
838
|
|
|
|
|
|
|
likewise autodetecting the encoding (UTF-8 or CP1252) of incoming messages. |
839
|
|
|
|
|
|
|
Since writing text with mixed encoding to a file, terminal, or database is |
840
|
|
|
|
|
|
|
not a good idea, you need a way to decode messages from IRC. |
841
|
|
|
|
|
|
|
L|/decode_irc> will do that. |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
=head2 Channel names |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
The matter is complicated further by the fact that some servers allow |
846
|
|
|
|
|
|
|
non-ASCII characters in channel names. IRC modules generally don't |
847
|
|
|
|
|
|
|
explicitly encode or decode any IRC traffic, but they do have to |
848
|
|
|
|
|
|
|
concatenate parts of a message (e.g. a channel name and a message) before |
849
|
|
|
|
|
|
|
sending it over the wire. So when you do something like |
850
|
|
|
|
|
|
|
C<< privmsg($channel, 'æði') >>, where C<$channel> is the unmodified |
851
|
|
|
|
|
|
|
channel name (a byte string) you got from an earlier IRC message, the |
852
|
|
|
|
|
|
|
channel name will get double-encoded when concatenated with your message (a |
853
|
|
|
|
|
|
|
non-ASCII text string) if the channel name contains non-ASCII bytes. |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
To prevent this, you can't simply L the channel name and |
856
|
|
|
|
|
|
|
then use it. C<'#æði'> in CP1252 is not the same channel as C<'#æði'> in |
857
|
|
|
|
|
|
|
UTF-8, since they are encoded as different sequences of bytes, and the IRC |
858
|
|
|
|
|
|
|
server only cares about the byte representation. Therefore, when using a |
859
|
|
|
|
|
|
|
channel name you got from the server (e.g. when replying to message), you |
860
|
|
|
|
|
|
|
should use the original byte string (before it has been decoded with |
861
|
|
|
|
|
|
|
L|/decode_irc>), and encode any other parameters (with |
862
|
|
|
|
|
|
|
L|Encode>) so that your message will be concatenated |
863
|
|
|
|
|
|
|
correctly. At some point, you'll probably want to print the channel name, |
864
|
|
|
|
|
|
|
write it to a log file or use it in a filename, so you'll eventually have to |
865
|
|
|
|
|
|
|
decode it, at which point the UTF-8 C<#æði> and CP1252 C<#æði> will have to |
866
|
|
|
|
|
|
|
be considered equivalent. |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
use Encode qw(encode_utf8 encode); |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
sub message_handler { |
871
|
|
|
|
|
|
|
# these three are all byte strings |
872
|
|
|
|
|
|
|
my ($nick, $channel, $message) = @_; |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# bad: if $channel has any non-ASCII bytes, they will get double-encoded |
875
|
|
|
|
|
|
|
privmsg($channel, 'æði'); |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
# bad: if $message has any non-ASCII bytes, they will get double-encoded |
878
|
|
|
|
|
|
|
privmsg('#æði', $message); |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
# good: both are byte strings already, so they will concatenate correctly |
881
|
|
|
|
|
|
|
privmsg($channel, $message); |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
# good: both are text strings (Latin1 as per Perl's default), so |
884
|
|
|
|
|
|
|
# they'll be concatenated correctly |
885
|
|
|
|
|
|
|
privmsg('#æði', 'æði'); |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
# good: similar to the last one, except now they're using UTF-8, which |
888
|
|
|
|
|
|
|
# means that the channel is actually not the same as above |
889
|
|
|
|
|
|
|
use utf8; |
890
|
|
|
|
|
|
|
privmsg('#æði', 'æði'); |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
# good: $channel and $msg_bytes are both byte strings |
893
|
|
|
|
|
|
|
my $msg_bytes = encode_utf8('æði'); |
894
|
|
|
|
|
|
|
privmsg($channel, $msg_bytes); |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
# good: $chan_bytes and $message are both byte strings |
897
|
|
|
|
|
|
|
# here we're sending a message to the utf8-encoded #æði |
898
|
|
|
|
|
|
|
my $utf8_bytes = encode_utf8('#æði'); |
899
|
|
|
|
|
|
|
privmsg($utf8_bytes, $message); |
900
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
# good: $chan_bytes and $message are both byte strings |
902
|
|
|
|
|
|
|
# here we're sending a message to the cp1252-encoded #æði |
903
|
|
|
|
|
|
|
my $cp1252_bytes = encode('cp1252', '#æði'); |
904
|
|
|
|
|
|
|
privmsg($cp1252_bytes, $message); |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
# bad: $channel is in an undetermined encoding |
907
|
|
|
|
|
|
|
log_message("Got message from $channel"); |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
# good: using the decoded version of $channel |
910
|
|
|
|
|
|
|
log_message("Got message from ".decode_irc($channel)); |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
See also L, L, |
914
|
|
|
|
|
|
|
L, L, and |
915
|
|
|
|
|
|
|
L. |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=head1 AUTHOR |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Hinrik Ern SigurEsson (C irc.perl.org, or C @ FreeNode). |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
Chris C Williams |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=head1 SEE ALSO |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
L |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
L |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
=cut |