-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathsignalpoller
More file actions
executable file
·200 lines (169 loc) · 6.77 KB
/
signalpoller
File metadata and controls
executable file
·200 lines (169 loc) · 6.77 KB
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
#!/usr/bin/perl -w
#
# Signal Poller for Hermod Gateway Bot.
#
# Keeping a tail on the tosignal file for messages to send to signal.
# Polling the signal group for new messages. These are send to telegram
# and irc
#
# 2019, Ruben de Groot
use strict;
use JSON;
use TOML;
use WWW::Curl::Easy;
use WWW::Curl::Form;
use URI::Escape;
use DBI;
use Capture::Tiny 'tee';
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";
}
unless (defined $cfg->{signal}->{phone} and defined $cfg->{signal}->{cli} and
defined $cfg->{signal}->{gid}) {
print "Please define signal->phone, signal->cli and signal->gidi\n";
print "Press <Ctrl>-C to continue\n";
sleep; exit;
}
my $sig = $cfg->{signal};
$ENV{JAVA_HOME} = $sig->{JAVA_HOME} if defined $sig->{JAVA_HOME};
open my $dbg, ">>", $sig->{debug} if defined $sig->{debug};
my $tel = $cfg->{telegram} if defined $cfg->{telegram};
my $irc = $cfg->{irc} if defined $cfg->{irc};
my $mat = $cfg->{matrix} if defined $cfg->{matrix};
my $mm = $cfg->{mattermost} if defined $cfg->{mattermost};
my $dis = $cfg->{discord} if defined $cfg->{discord};
# tailing signal infile and telegram downloads for stuff to send
open my $tail, "<", $sig->{infile} or die @_;
my $inode = (stat($sig->{infile}))[1];
# SEEK_END
seek($tail, 0, 2) or die @_;
for (;;) {
sleep 10; # not to get too tight loop
# check if logfiles haven't turned over below our feet
if ($inode != (stat($sig->{infile}))[1]) {
close $tail;
$inode = (stat($sig->{infile}))[1];
open($tail,$sig->{infile}) or next;
} else {
# SEEK_CUR
seek($tail, 0, 1);
}
# send new messages to signal group
my $msg ='';
while (my $line = <$tail>) {
if ($line =~ /^FILE!/) {
# send photo's, documents
$line = substr $line,5;
my ($fileinfo,$caption) = split / /, $line, 2;
my ($url,$mime,$file) = split /!/, $fileinfo;
my ($out, $err, $ret) = tee {
system($sig->{cli},"-u",$sig->{phone},"send","-g",$sig->{gid},
"-m","$caption","-a","$file");
};
print $dbg $out, $err if defined $dbg;
notify($err) if $err;
} else {
$msg .= $line;
}
}
if ($msg) {
my $text = '';
$text .= decode_utf8($msg, Encode::FB_QUIET) while $msg;
my ($out, $err, $ret) = tee {
system($sig->{cli},"-u",$sig->{phone},"send","-g", $sig->{gid}, "-m", "$text");
};
print $dbg $out, $err if defined $dbg;
notify($err) if $err;
}
# now poll the signal group for new messages
my $json = JSON->new->allow_nonref;
my ($out, $err, $ret) = tee {
system($sig->{cli},"-u",$sig->{phone},"--output=json","receive");
};
print $dbg $out, $err if defined $dbg;
if ($err) {
notify($err);
#rely on a restart
exit;
}
my @lines = split /\n/, $out;
for my $line (@lines) {
my $sav;
print $dbg $line if defined $dbg;
my $sigmsg = $json->decode($line);
next unless defined $sigmsg->{envelope}->{dataMessage};
$sigmsg = $sigmsg->{envelope};
my $datamsg = $sigmsg->{dataMessage};
my $sender = (defined $sigmsg->{sourceName}) ? $sigmsg->{sourceName} : "Anonymous Pirat";
my $group = (defined $datamsg->{groupInfo}->{groupId}) ? $datamsg->{groupInfo}->{groupId} : "";
my $attach = (defined $datamsg->{attachments}) ? $datamsg->{attachments} : undef;
my $msg = (defined $datamsg->{message}) ? $datamsg->{message} : "";
my $text = ''; $sav = $msg;
eval { $text .= decode_utf8($msg, Encode::FB_QUIET) while $msg; };
$text = $sav if $@; # try undecoded string as last resort
my $apidata->{text} = $sav;
# is the message a quote
my $quote = "";
if (defined $datamsg->{quote}{text}) {
my $qtext = $datamsg->{quote}{text}; chomp $qtext;
$apidata->{quote} = $qtext;
$qtext = (length($qtext) > 100) ? "(reply to ". substr($qtext,0,100) ."...)\n\n" : "(reply to: $qtext)\n\n";
$sav = $qtext;
eval { $quote .= decode_utf8($qtext, Encode::FB_QUIET) while $qtext; };
$quote = $sav if $@; # try undecoded string as last resort
}
# only relay group messages with contents
next unless $group eq $sig->{gid};
next unless $text or $attach;
# dry-run
next if $sig->{dryrun};
# relay to all chats
$apidata->{sender} = "$sender on signal";
my $pre = "[sig] $sender: ";
if ($text =~ /\S/) {
Hermod::relay2tel($tel,"$pre$quote$text\n",$dbg) if defined $tel;
Hermod::relay2irc("$quote$text\n",$irc,$pre,$dbg) if defined $irc;
Hermod::relay2mtx("$pre$quote$text\n",$mat,$dbg) if defined $mat;
Hermod::relay2dis("$pre$quote$text\n",$dis,$dbg) if defined $dis;
}
# relay optional attachments
foreach my $att (@$attach) {
(my $ext = $att->{contentType}) =~ s#.*/##;
rename "$sig->{attachments}/$att->{id}", "$sig->{attachments}/$att->{id}.$ext";
my $type = ($att->{contentType} =~ /image/) ? 'photo' : 'document';
my $filemsg = "FILE!$sig->{url}/$att->{id}.$ext!$att->{contentType}!$sig->{attachments}/$att->{id}.$ext [sig] $type by $sender\n";
my $msg = "[sig] **$sender sends a $type: $sig->{url}/$att->{id}.$ext\n";
push @{$apidata->{files}}, $sig->{attachments}/$att->{id}.$ext;
Hermod::relayFile2tel($filemsg,$tel,$type,$dbg) if defined $tel;
Hermod::relay2mtx($msg,$mat,$dbg) if defined $mat;
Hermod::relay2dis($msg,$dis,$dbg) if defined $dis;
Hermod::relayToFile($msg, $irc->{infile}, $dbg) if defined $irc;
}
Hermod::relay2mmapi($apidata, $mm, $dbg) if $apidata->{text} or $apidata->{files};
}
}
sub notify {
my $err = shift;
my $msg = '';
my $hostname = qx( hostname ); chomp $hostname;
# filters here
if ($err =~ /Failed to send \(some\) messages:/) {
while ($err =~ /Untrusted Identity for "(\+[\d]+)"/g) {
unless (defined $sig->{unreg}->{$1}) {
$sig->{unreg}->{$1} = 1;
$msg .= " $1";
}
}
$msg = "Untrusted users: $msg\n" if $msg;
} else {
$msg = $err;
}
# notify daily on errors
return if defined $sig->{errors}->{$msg} and $sig->{errors}->{$msg} > time - 86400;
$sig->{errors}->{$msg} = time;
qx( $cfg->{common}->{notify} "Signalpoller on $hostname: $msg" ) if defined $cfg->{common}->{notify} and $msg;
}