-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathircbot
executable file
·352 lines (286 loc) · 11.2 KB
/
ircbot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
#!/usr/bin/perl
#
# IRC Bot for Hermod Telegram Gateway. Reads from a logfile to be send to irc and relays
# messages from irc to telegram and to another logfile which is send to a signal group
# by signalpoller.
#
# 2019, Ruben de Groot
use strict;
use POE qw(Component::IRC Component::IRC::Plugin::FollowTail Component::IRC::Plugin::Connector
Component::IRC::Plugin::BotAddressed Component::SSLify);
use URI::Escape;
use JSON qw( decode_json );
use TOML;
use DBI;
use Hermod;
use Encode qw( decode_utf8 );
open my $fh, '<', "/etc/hermod.toml" or die "error opening configuration $!";
my ($cfg, $e) = from_toml do { local $/; <$fh> };
unless ($cfg) {
die "Error parsing toml: $e";
}
open my $dbg, ">>", $cfg->{irc}->{debug} if defined $cfg->{irc}->{debug};
my $tel = $cfg->{telegram} if defined $cfg->{telegram};
my $sig = $cfg->{signal} if defined $cfg->{signal};
my $mat = $cfg->{matrix} if defined $cfg->{matrix};
my $mm = $cfg->{mattermost} if defined $cfg->{mattermost};
my $dis = $cfg->{discord} if defined $cfg->{discord};
my $URL = "https://api.telegram.org/bot$tel->{token}/sendMessage";
# We create a new PoCo-IRC object
my $irc = POE::Component::IRC->spawn(
nick => $cfg->{irc}->{nick},
ircname => $cfg->{irc}->{ircname},
password => $cfg->{irc}->{password},
username => $cfg->{irc}->{ident},
server => $cfg->{irc}->{node},
Port => $cfg->{irc}->{port},
UseSSL => $cfg->{irc}->{UseSSL},
) or die "Oh noooo! $!";
POE::Session->create(
package_states => [
main => [ qw(_default _start irc_001 irc_public irc_msg irc_join irc_quit irc_nick irc_tail_input irc_ctcp_action irc_353 irc_bot_addressed irc_bot_mentioned lag_o_meter) ],
],
heap => { irc => $irc },
);
$poe_kernel->run();
sub _start {
my ($kernel, $heap) = @_[KERNEL ,HEAP];
$heap->{connector} = POE::Component::IRC::Plugin::Connector->new();
$irc->plugin_add( 'Connector' => $heap->{connector} );
# file to be read and send to the channel
$irc->plugin_add( FollowTail => POE::Component::IRC::Plugin::FollowTail->new(
filename => $cfg->{irc}->{infile},
));
# respond when we are mentioned or addressed
$irc->plugin_add( BotAddressed => POE::Component::IRC::Plugin::BotAddressed->new() );
$irc->yield( register => 'all' );
$irc->yield( connect => { } );
$kernel->delay( 'lag_o_meter' => 60 );
return;
}
sub lag_o_meter {
my ($kernel,$heap) = @_[KERNEL,HEAP];
print 'Time: ' . time() . ' Lag: ' . $heap->{connector}->lag() . "\n";
$kernel->delay( 'lag_o_meter' => 60 );
return;
}
sub irc_001 {
my $sender = $_[SENDER];
my $irc = $sender->get_heap();
print "Connected to ", $irc->server_name(), "\n";
print $dbg "Connected to ", $irc->server_name(), "\n" if defined $dbg;
# we join our channel
$irc->yield( join => $cfg->{irc}->{channel} );
}
sub irc_public {
# handle normal messages in irc channel
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
my $nick = ( split /!/, $who )[0];
# catch commands
if ($what =~ /^!users/i) {
# send names to IRC, split lines in chunks of ~maxmsg size if necessary
for my $text (teleusers(),signalusers()) {
next unless $text;
if (length $text > $cfg->{irc}->{maxmsg}) {
$text =~ s/(.{1,$cfg->{irc}->{maxmsg}}\S|\S+)\s+/$1\n/g;
$irc->yield( 'privmsg', $cfg->{irc}->{channel}, "$_" ) for split /\n/, $text;
} else {
$irc->yield( 'privmsg', $cfg->{irc}->{channel}, "$text" );
}
}
return;
}
my $msg = "[irc] $nick: $what\n";
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
Hermod::relay2tel($tel,$text,$dbg);
Hermod::relay2mm($text,$mm,$dbg);
Hermod::relay2mtx($text,$mat,$dbg);
Hermod::relay2dis($text,$dis,$dbg);
Hermod::relay2sig($text,$sig,$dbg);
}
sub irc_msg {
# handle private messages
my ($who, $what) = @_[ARG0, ARG2];
my $nick = ( split /!/, $who )[0];
if ($what =~ /^!users/) {
# send names to nick, split lines in chunks of ~maxmsg size if necessary
for my $text (teleusers(),signalusers()) {
next unless $text;
if (length $text > $cfg->{irc}->{maxmsg}) {
$text =~ s/(.{1,$cfg->{irc}->{maxmsg}}\S|\S+)\s+/$1\n/g;
$irc->yield( 'privmsg', $nick, "$_" ) for split /\n/, $text;
} else {
$irc->yield( 'privmsg', $nick, "$text" );
}
}
return;
}
# send apologies
$irc->yield( 'privmsg', $nick, "Hi, $nick, I don't do private messages, except:" );
$irc->yield( 'privmsg', $nick, "!users - list users in other channels (telegram, signal)" );
}
sub irc_ctcp_action {
# handle /me events
my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2];
my $nick = ( split /!/, $who )[0];
my $msg = "[irc] ***$nick $what\n";
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
Hermod::relay2tel($tel,$text,$dbg);
Hermod::relay2mm($text,$mm,$dbg);
Hermod::relay2mtx($text,$mat,$dbg);
Hermod::relay2dis($text,$dis,$dbg);
Hermod::relay2sig($text,$sig,$dbg);
}
sub irc_nick {
# handle /nick events
my ($whowas,$who) = @_[ARG0, ARG1];
my $nick = ( split /!/, $whowas )[0];
my $msg = "[irc] $nick is now known as $who\n";
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
Hermod::relay2tel($tel,$text,$dbg);
Hermod::relay2mm($text,$mm,$dbg);
Hermod::relay2mtx($text,$mat,$dbg);
Hermod::relay2dis($text,$dis,$dbg);
Hermod::relay2sig($text,$sig,$dbg);
}
sub irc_join {
# someone joins the channel
my ($who,$channel) = @_[ARG0, ARG1];
my $nick = ( split /!/, $who )[0];
return if $nick eq $cfg->{irc}->{nick};
my $msg = "[irc] $nick joined the chat\n";
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
if (defined $cfg->{irc}->{showjoin}) {
Hermod::relay2tel($tel,$text,$dbg);
Hermod::relay2mm($text,$mm,$dbg);
Hermod::relay2mtx($text,$mat,$dbg);
Hermod::relay2dis($text,$dis,$dbg);
Hermod::relay2sig($text,$sig,$dbg);
}
}
sub irc_quit {
# someone leaves the channel
my ($who,$msg) = @_[ARG0, ARG1];
my $nick = ( split /!/, $who )[0];
my $msg = "[irc] $nick quit the chat ($msg)\n";
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
if (defined $cfg->{irc}->{showquit}) {
Hermod::relay2tel($tel,$text,$dbg);
Hermod::relay2mm($text,$mm,$dbg);
Hermod::relay2mtx($text,$mat,$dbg);
Hermod::relay2dis($text,$dis,$dbg);
Hermod::relay2sig($text,$sig,$dbg);
}
}
sub irc_353 {
# RPL_NAMREPLY
my ($names) = @_[ARG1];
$names =~ s/:(.*)$/$1/;
my @users = split / /, $names;
my $msg = "IRC users: " . (join ', ', @users[2..$#users]) . "\n";
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
if (defined $cfg->{irc}->{names_asked} and $cfg->{irc}->{names_asked} eq 'TEL') {
$text .= "Signal users: " . signalusers() . "\n" if defined $sig->{db};
Hermod::relay2tel($tel,$text);
} elsif (defined $cfg->{irc}->{names_asked} and $cfg->{irc}->{names_asked} eq 'SIG') {
$text .= "Telegram users: " . teleusers() . "\n" if defined $tel->{db};
Hermod::relay2sig($text,$sig,$dbg);
}
}
sub irc_tail_input {
# handle text added to irc->infile
my ($kernel, $sender, $input) = @_[KERNEL, SENDER, ARG1];
next unless $input;
# check for files
if ($input =~ /^FILE!/) {
$input = substr $input,5;
my ($fileinfo,$caption) = split / /, $input, 2; chomp $caption;
my ($url,$mime) = split /!/, $fileinfo;
$kernel->post( $sender, 'privmsg', $cfg->{irc}->{channel}, "$caption: $url\n" );
# check for commands
} elsif ($input =~ /^CMD!TEL!users/) {
$irc->yield( names => $cfg->{irc}->{channel} );
$cfg->{irc}->{names_asked} = 'TEL';
} elsif ($input =~ /^CMD!SIG!users/) {
$irc->yield( names => $cfg->{irc}->{channel} );
$cfg->{irc}->{names_asked} = 'SIG';
# send the message as is
} else {
$kernel->post( $sender, 'privmsg', $cfg->{irc}->{channel}, $input );
}
}
sub irc_bot_addressed {
# Reply when someone addresses me
my ($sender, $what) = @_[SENDER, ARG2];
my $nick = ( split /!/, $_[ARG0] )[0];
return unless defined $cfg->{irc}->{respond};
my $text = "Hi $nick, you addressed me, but I'm just a messenger bot, please don't shoot me. I relay messages between IRC, Telegram, Signal, Mattermost and Matrix users. You can see the name of the person who spoke to you directly after the [tel], [sig], [mm] or [mtx] tag.";
$irc->yield( 'privmsg', $cfg->{irc}->{channel}, "$text" );
print $dbg "$nick addressed me with the message '$what'\n" if defined $dbg;
}
sub irc_bot_mentioned {
my ($sender, $what) = @_[SENDER, ARG2];
my ($nick) = ( split /!/, $_[ARG0] )[0];
return unless defined $cfg->{irc}->{respond};
my $text = "Hi $nick, you mentioned me. I'm a messenger bot, don't shoot me. I relay messages between IRC, Telegram, Signal, Mattermost and Matrix users. You can see the name of the person who spoke to you directly after the [tel], [sig], [mm] or [mtx] tag.";
$irc->yield( 'privmsg', $cfg->{irc}->{channel}, "$text" );
print $dbg "$nick mentioned my name with the message '$what'\n" if defined $dbg;
}
sub _default {
# This will produce some debug info.
my ($event, $args) = @_[ARG0 .. $#_];
my @output = ( "$event: " );
for my $arg (@$args) {
if ( ref $arg eq 'ARRAY' ) {
push( @output, '[' . join(', ', @$arg ) . ']' );
}
else {
push ( @output, "'$arg'" );
}
}
print join ' ', @output, "\n";
print $dbg join ' ', @output, "\n" if defined $dbg;
if (grep(/irc_disconnect/,@output)) {
my $hostname = qx( hostname -f );
qx( $cfg->{common}->{notify} "Hermod died on $hostname\n" ) if defined $cfg->{common}->{notify};
print "I got disconnected :( :(\nKilling myself..\n";
exit(1);
}
}
sub teleusers {
# returns list of telegram users as string
my $users = '';
return $users unless defined $tel;
eval {
my $dbh = DBI->connect("dbi:SQLite:dbname=$tel->{db}", "", "", { RaiseError => 1 }, );
my $st = $dbh->prepare("select * from teleusers");
$st->execute();
while (my $row = $st->fetchrow_hashref()) {
$users .= ', ' if $users;
$users .= "$row->{first_name}";
$users .= " $row->{last_name}" if defined $row->{last_name};
}
};
return "Telegram users: $users\n";
}
sub signalusers {
# returns list of signal users as string
my $users = '';
return $users unless defined $sig;
eval {
my $dbh = DBI->connect("dbi:SQLite:dbname=$sig->{db}", "", "", { RaiseError => 1 }, );
my $st = $dbh->prepare("select * from alias");
$st->execute();
while (my $row = $st->fetchrow_hashref()) {
$users .= ', ' if $users;
$users .= "$row->{nick}";
}
};
return "Signal users: $users\n";
}