#!/usr/bin/perl use strict; use warnings; use Email::Sender::Simple qw(sendmail); use Sipwise::Provisioning::Billing; my %LOCK = ( none => undef, foreign => 1, outgoing => 2, incoming => 3, global => 4, 0 => 'none', 1 => 'foreign', 2 => 'outgoing', 3 => 'incoming', 4 => 'global', ); my $conf = Sipwise::Provisioning::Config->new()->get_config(); my $o = Sipwise::Provisioning::Billing->new(); my $db = $o->{database}; my $a = $db->sql_get_all_arrayref(<<"!"); SELECT 'profile_limit' as type, cdr.source_account_id, SUM(cdr.source_customer_cost) AS daily_cost, bpinfo.contract_id, bpinfo.fraud_daily_limit, bpinfo.fraud_daily_lock, bpinfo.fraud_daily_notify FROM ( SELECT contracts.id as contract_id, billing_profiles.fraud_daily_limit, billing_profiles.fraud_daily_lock, billing_profiles.fraud_daily_notify FROM billing.contracts JOIN billing.billing_mappings ON contracts.id = billing_mappings.contract_id JOIN billing.billing_profiles ON (billing_profiles.id = (SELECT m.billing_profile_id FROM billing.billing_mappings m WHERE ((m.start_date IS NULL) OR (m.start_date <= NOW())) AND ((m.end_date IS NULL) OR (m.end_date >= NOW())) AND (m.contract_id = contracts.id) ORDER BY m.start_date DESC LIMIT 1 ) ) WHERE (contracts.status = 'active') AND (billing_profiles.fraud_daily_limit IS NOT NULL) AND (billing_profiles.fraud_daily_limit > 0) ) as bpinfo JOIN accounting.cdr ON cdr.source_account_id = bpinfo.contract_id WHERE (DATEDIFF(FROM_UNIXTIME(cdr.start_time), CURDATE()) = 0) GROUP BY cdr.source_account_id HAVING (daily_cost > bpinfo.fraud_daily_limit) UNION ALL SELECT 'account_limit' as type, cdr.source_account_id, SUM(cdr.source_customer_cost) AS daily_cost, contracts.id, contract_fraud_preferences.fraud_daily_limit, contract_fraud_preferences.fraud_daily_lock, contract_fraud_preferences.fraud_daily_notify FROM accounting.cdr JOIN billing.contracts ON cdr.source_account_id = contracts.id JOIN billing.contract_fraud_preferences ON contracts.id = contract_fraud_preferences.contract_id WHERE (contracts.status = 'active') AND (DATEDIFF(FROM_UNIXTIME(cdr.start_time), CURDATE()) = 0) GROUP BY cdr.source_account_id ! my $x = {}; for my $e (@{ $a }) { if(exists $x->{$e->{id}}) { if($x->{$e->{id}}->{type} eq 'profile_limit' && $e->{type} eq 'account_limit') { # if account limit hits and it has lock and/or notify, mark for action if(defined $e->{fraud_daily_limit} and int($e->{daily_cost}) >= int($e->{fraud_daily_limit}) and ($e->{fraud_daily_lock} || $e->{fraud_daily_notify})) { $x->{$e->{id}} = $e; } else { # we have account fraud prefs set, but either the limit is not reached # or no actions are necessary, let it slip through, overriding # billing profile fraud settings delete $x->{$e->{id}}; } } } else { # if account or billing profile limit hits and it has lock and/or notify, # mark for action if(defined $e->{fraud_daily_limit} and int($e->{daily_cost}) >= int($e->{fraud_daily_limit}) and ($e->{fraud_daily_lock} || $e->{fraud_daily_notify})) { $x->{$e->{id}} = $e; } } } for my $e (values %{ $x }) { $e->{fraud_daily_lock} and $o->lock_voip_account({id => $e->{id}, lock => $LOCK{$e->{fraud_daily_lock}}}); $e->{fraud_daily_notify} or next; my $subs = $db->sql_get_all_arrayref(<<"!", $e->{id}); SELECT s.username, d.domain, s.external_id FROM voip_subscribers s LEFT JOIN domains d ON d.id = s.domain_id WHERE s.contract_id = ? AND s.status != 'terminated' ! my $cur = sprintf('%.2f', $e->{daily_cost} / 100); my $max = sprintf('%.2f', $e->{fraud_daily_limit} / 100); my ($subject, $body); if ($e->{fraud_daily_lock}) { $body = "Account ID " . $e->{id} . " has been locked due to exceeding the configured" . "\n" . "daily credit balance threshold ($cur >= $max ) in the " . ($e->{type} eq 'profile_limit' ? 'billing profile' : 'account settings') . "\n\n"; $subject = 'Account ID ' . $e->{id} . ' locked by daily fraud detection'; } else { $body = "Account ID " . $e->{id} . " is currently exceeding the configured daily credit balance" . "\n" . "threshold ($cur >= $max) in the " . ($e->{type} eq 'profile_limit' ? 'billing profile' : 'account settings') . ",\n" . "but has not been locked due to configuration.\n\n"; $subject = 'Account ID ' . $e->{id} . ' exceeding daily fraud detection limit'; } if (!$subs || !@$subs) { $body .= "There are no affected subscribers.\n"; } else { $body .= "Affected subscribers:\n"; for my $s (@$subs) { $body .= "\t$s->{username}\@$s->{domain}". ($s->{external_id} ? " (external ID '$s->{external_id}')" : '') . "\n"; } } sendmail ( Email::Simple->create( header => [ To => $e->{fraud_daily_notify}, From => $$conf{adminmail}, Subject => $subject, ], body => $body, )); } 1;