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
|
#!/usr/bin/perl
use strict;
use Cpanel::JSON::XS;
use Getopt::Long;
use LWP::UserAgent;
use MIME::Base64;
use Net::HTTPS::Any qw(https_post https_get);
use FS::UID qw(adminsuidsetup);
use FS::Record qw(qsearchs);
use FS::cdr;
use FS::cdr_batch;
sub usage {
"Usage:
freeside-cdr-conexiant-import -h -u username -p apikey [-v] freesideuser
Downloads any existing CDR files with the BilledCallsOnly flag and
imports records that have not been imported yet. Silently skips
records that have already been imported.
";
}
# should really be using a module for this
`which unzip` or die "can't find unzip executable";
my ($username,$password,$verbose);
GetOptions(
"password=s" => \$password,
"username=s" => \$username,
"verbose" => \$verbose,
);
my $fsuser = $ARGV[-1];
die usage() unless $fsuser;
adminsuidsetup($fsuser);
my ( $page, $response, %reply_headers ) = https_post(
'host' => 'api.conexiant.net',
'port' => '443',
'path' => '/v1/Cdrs/SearchCdrsDownloads',
'headers' => {
'Authorization' => 'Basic ' . MIME::Base64::encode("$username:$password",'')
},
'content' => '{}',
);
die "Bad response from conexiant server: $response"
unless $response =~ /^200/;
my $result = decode_json($page);
die "Error from conexiant: " . ($result->{'ErrorInfo'} || 'No error message')
unless $result->{'Success'};
my $files = $result->{'Data'}->{'Result'};
die "Unexpected results from conexiant, not an array"
unless ref($files) eq 'ARRAY';
my $dir = $FS::UID::cache_dir. "/cache.". $FS::UID::datasrc;
my $ua = LWP::UserAgent->new;
# Download files are created automatically at regular frequent intervals,
# but they contain overlapping data.
#
# FS::cdr::conexiant automatically skips previously imported cdrs,
# though if it does so for all records in a file,
# then batch_import thinks the file is empty
foreach my $file (@$files) {
next unless $file->{'BilledCallsOnly'};
my $cdrbatch = 'conexiant-' . $file->{'Identifier'};
# files that were "empty" will unfortunately be re-downloaded,
# but the alternative is to leave an excess of empty batches in system,
# and re-downloading is harmless (all files expire after 48 hours anyway)
if (qsearchs('cdr_batch',{ 'cdrbatch' => $cdrbatch })) {
print "$cdrbatch already imported\n" if $verbose;
next;
}
if ($verbose) {
print "Downloading $cdrbatch\n".
" Created ".$file->{'CreatedOn'}."\n".
" Start ".$file->{'QueryStart'}."\n".
" End ".$file->{'QueryEnd'}."\n".
" Link ".$file->{'ValidLink'}."\n";
}
my $zfh = new File::Temp( TEMPLATE => 'conexiant.XXXXXXXX',
SUFFIX => '.zip',
DIR => $dir,
)
or die "can't open temporary file to store download: $!\n";
my $cfh = new File::Temp( TEMPLATE => 'conexiant.XXXXXXXX',
SUFFIX => '.csv',
DIR => $dir,
)
or die "can't open temporary file to unzip download: $!\n";
# yeah, these files ain't secured in any way
my $response = $ua->get($file->{'ValidLink'}, ':content_file' => $zfh->filename);
unless ($response->is_success) {
die "Error downloading $cdrbatch: ".$response->status_line;
}
my $zfilename = $zfh->filename;
print $cfh `unzip -p $zfilename 'Conexiant Cdrs.csv'`;
seek($cfh,0,0);
print "Importing batch $cdrbatch\n" if $verbose;
my $error = FS::cdr::batch_import({
'batch_namevalue' => $cdrbatch,
'file' => $cfh->filename,
'format' => 'conexiant'
});
if ($error eq 'Empty file!') {
print "File contains no new cdrs, no batch created\n" if $verbose;
$error = '';
} elsif ($verbose && !$error) {
print "File successfully imported\n";
}
die "Error importing $cdrbatch: $error" if $error;
}
exit;
|