diff --git a/config-schema.dhall b/config-schema.dhall index 1e6f5eda9b7a7e1fcc9d441b3180cfb6015467bc..9878775bd435b06752207207e1176e513164036f 100644 --- a/config-schema.dhall +++ b/config-schema.dhall @@ -39,6 +39,7 @@ , sgx : Text , sip : { app : Text, realm : Text } , sip_host : Text +, unbilled_targets : List Text , upstream_domain : Text , web : < Inet : { interface : Text, port : Natural } | Unix : Text > , web_register : { from : Text, to : Text } diff --git a/config.dhall.sample b/config.dhall.sample index 61b0e87b7696af2d85c995a912f0ed2ac43293dc..cedfc1b832cd67146a310fd26ab5f160795415cf 100644 --- a/config.dhall.sample +++ b/config.dhall.sample @@ -76,6 +76,7 @@ in payable = "", notify_from = "+15551234567@example.net", admins = ["test\\40example.com@example.net"], + unbilled_targets = ["+14169938000"], upstream_domain = "example.net", approved_domains = toMap { `example.com` = Some "customer_id" } } diff --git a/lib/trust_level.rb b/lib/trust_level.rb index 285850ff77dd2e9722f4f669be469a5cdeaeafc0..c70edeb92de07261af31ee26a308c5f477705f46 100644 --- a/lib/trust_level.rb +++ b/lib/trust_level.rb @@ -27,6 +27,10 @@ module TrustLevel def support_call?(*) false end + + def send_message?(*) + false + end end class Basement @@ -37,6 +41,10 @@ module TrustLevel def support_call?(rate, concurrency) rate <= 0.02 && concurrency < 1 end + + def send_message?(messages_today) + messages_today < 200 + end end class Paragon @@ -47,6 +55,10 @@ module TrustLevel def support_call?(_, concurrency) concurrency < 10 end + + def send_message?(messages_today) + messages_today < 700 + end end class Customer @@ -70,5 +82,9 @@ module TrustLevel def support_call?(rate, concurrency) rate <= @max_rate && concurrency < 4 end + + def send_message?(messages_today) + messages_today < 500 + end end end diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 5cc1d1da03d39d9f53a8be967e623f80c9797053..be6cbf60462a24ba756f7f997875fc962500dc15 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -307,42 +307,60 @@ end # Especially if we have the component join MUC for notifications message(type: :groupchat) { true } +UNBILLED_TARGETS = Set.new(CONFIG[:unbilled_targets]) def billable_message(m) - (m.body && !m.body.empty?) || m.find("ns:x", ns: OOB.registered_ns).first + b = m.body + !UNBILLED_TARGETS.member?(m.to.node) && \ + (b && !b.empty? || m.find("ns:x", ns: OOB.registered_ns).first) end -def notify_admin_of_usage(customer, usage, today) - ExpiringLock.new("jmp_usage_notify-#{customer.customer_id}").with do - BLATHER.join(CONFIG[:notify_admin], "sgx-jmp") - BLATHER.say( - CONFIG[:notify_admin], "#{customer.customer_id} has used " \ - "#{usage} messages since #{today - 30}", :groupchat - ) +class OverLimit < StandardError + def initialize(customer, usage) + super("Please contact support") + @customer = customer + @usage = usage + end + + def notify_admin + ExpiringLock.new("jmp_usage_notify-#{@customer.customer_id}").with do + BLATHER.join(CONFIG[:notify_admin], "sgx-jmp") + BLATHER.say( + CONFIG[:notify_admin], "#{@customer.customer_id} has used " \ + "#{@usage} messages today", :groupchat + ) + end end end +class CustomerExpired < StandardError; end + message do |m| StatsD.increment("message") sentry_hub = new_sentry_hub(m, name: "message") today = Time.now.utc.to_date - CustomerRepo - .new(set_user: sentry_hub.current_scope.method(:set_user)) + CustomerRepo.new(set_user: sentry_hub.current_scope.method(:set_user)) .find_by_jid(m.from.stripped).then { |customer| + next customer.stanza_from(m) unless billable_message(m) + + if customer.plan_name && !customer.active? + raise CustomerExpired, "Your account is expired, please top up" + end + EMPromise.all([ - (customer.incr_message_usage if billable_message(m)), - customer.message_usage((today..(today - 30))).then do |usage| - if usage < 4500 - customer.stanza_from(m) - else - BLATHER << m.as_error( - "policy-violation", :wait, "Please contact support" - ) - end - notify_admin_of_usage(customer, usage, today) if usage > 900 - end - ]) - }.catch_only(CustomerRepo::NotFound) { |e| + TrustLevelRepo.new.find(customer), + customer.message_usage((today..today)) + ]).then { |(tl, usage)| + raise OverLimit.new(customer, usage) unless tl.send_message?(usage) + }.then do + EMPromise.all([ + customer.incr_message_usage, customer.stanza_from(m) + ]) + end + }.catch_only(OverLimit) { |e| + e.notify_admin + BLATHER << m.as_error("policy-violation", :wait, e.message) + }.catch_only(CustomerRepo::NotFound, CustomerExpired) { |e| BLATHER << m.as_error("forbidden", :auth, e.message) }.catch { |e| panic(e, sentry_hub) } end