2016-10-19 03:11:36 +00:00
|
|
|
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
|
2013-06-12 15:59:58 +00:00
|
|
|
|
2012-09-20 12:08:02 +00:00
|
|
|
class Channel < ApplicationModel
|
2015-08-30 11:58:05 +00:00
|
|
|
load 'channel/assets.rb'
|
|
|
|
include Channel::Assets
|
|
|
|
|
2016-06-19 16:58:41 +00:00
|
|
|
belongs_to :group, class_name: 'Group'
|
|
|
|
|
2012-04-13 13:51:10 +00:00
|
|
|
store :options
|
2015-08-29 11:46:48 +00:00
|
|
|
store :preferences
|
2012-04-13 13:51:10 +00:00
|
|
|
|
2015-08-28 08:19:27 +00:00
|
|
|
after_create :email_address_check
|
|
|
|
after_update :email_address_check
|
|
|
|
after_destroy :email_address_check
|
|
|
|
|
2016-01-09 12:23:11 +00:00
|
|
|
# rubocop:disable Style/ClassVars
|
|
|
|
@@channel_stream = {}
|
2017-10-02 11:23:14 +00:00
|
|
|
@@channel_stream_started_till_at = {}
|
2016-01-09 12:23:11 +00:00
|
|
|
# rubocop:enable Style/ClassVars
|
|
|
|
|
2015-08-28 00:53:14 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
fetch all accounts
|
|
|
|
|
|
|
|
Channel.fetch
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2012-04-10 14:06:46 +00:00
|
|
|
def self.fetch
|
2015-08-28 00:53:14 +00:00
|
|
|
channels = Channel.where('active = ? AND area LIKE ?', true, '%::Account')
|
|
|
|
channels.each(&:fetch)
|
2012-04-10 14:06:46 +00:00
|
|
|
end
|
2015-08-28 00:53:14 +00:00
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
fetch one account
|
|
|
|
|
|
|
|
channel = Channel.where(area: 'Email::Account').first
|
|
|
|
channel.fetch
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2016-01-10 13:24:54 +00:00
|
|
|
def fetch(force = false)
|
2015-08-28 00:53:14 +00:00
|
|
|
|
|
|
|
adapter = options[:adapter]
|
|
|
|
adapter_options = options
|
2015-08-30 11:58:05 +00:00
|
|
|
if options[:inbound] && options[:inbound][:adapter]
|
2015-08-28 00:53:14 +00:00
|
|
|
adapter = options[:inbound][:adapter]
|
|
|
|
adapter_options = options[:inbound][:options]
|
|
|
|
end
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
# we need to require each channel backend individually otherwise we get a
|
|
|
|
# 'warning: toplevel constant Twitter referenced by Channel::Driver::Twitter' error e.g.
|
|
|
|
# so we have to convert the channel name to the filename via Rails String.underscore
|
|
|
|
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
|
|
|
|
require "channel/driver/#{adapter.to_filename}"
|
|
|
|
|
|
|
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
|
|
|
driver_instance = driver_class.new
|
2016-01-10 13:24:54 +00:00
|
|
|
return if !force && !driver_instance.fetchable?(self)
|
2015-09-06 07:50:51 +00:00
|
|
|
result = driver_instance.fetch(adapter_options, self)
|
|
|
|
self.status_in = result[:result]
|
|
|
|
self.last_log_in = result[:notice]
|
2016-01-09 12:23:11 +00:00
|
|
|
preferences[:last_fetch] = Time.zone.now
|
2017-10-02 11:23:14 +00:00
|
|
|
save!
|
2016-01-09 12:23:11 +00:00
|
|
|
rescue => e
|
|
|
|
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
|
|
|
|
logger.error error
|
|
|
|
logger.error e.backtrace
|
|
|
|
self.status_in = 'error'
|
|
|
|
self.last_log_in = error
|
|
|
|
preferences[:last_fetch] = Time.zone.now
|
2017-10-02 11:23:14 +00:00
|
|
|
save!
|
2016-01-09 12:23:11 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
stream instance of account
|
|
|
|
|
|
|
|
channel = Channel.where(area: 'Twitter::Account').first
|
|
|
|
stream_instance = channel.stream_instance
|
|
|
|
|
|
|
|
# start stream
|
|
|
|
stream_instance.stream
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def stream_instance
|
|
|
|
|
|
|
|
adapter = options[:adapter]
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
|
|
|
# we need to require each channel backend individually otherwise we get a
|
|
|
|
# 'warning: toplevel constant Twitter referenced by Channel::Driver::Twitter' error e.g.
|
|
|
|
# so we have to convert the channel name to the filename via Rails String.underscore
|
|
|
|
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
|
|
|
|
require "channel/driver/#{adapter.to_filename}"
|
|
|
|
|
|
|
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
|
|
|
driver_instance = driver_class.new
|
|
|
|
|
|
|
|
# check is stream exists
|
|
|
|
return if !driver_instance.respond_to?(:stream_instance)
|
|
|
|
driver_instance.stream_instance(self)
|
|
|
|
|
|
|
|
# set scheduler job to active
|
|
|
|
|
|
|
|
return driver_instance
|
2015-08-28 00:53:14 +00:00
|
|
|
rescue => e
|
|
|
|
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
|
|
|
|
logger.error error
|
|
|
|
logger.error e.backtrace
|
|
|
|
self.status_in = 'error'
|
|
|
|
self.last_log_in = error
|
2017-10-02 11:23:14 +00:00
|
|
|
save!
|
2015-08-28 00:53:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2016-01-09 12:23:11 +00:00
|
|
|
stream all accounts
|
|
|
|
|
|
|
|
Channel.stream
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def self.stream
|
|
|
|
Thread.abort_on_exception = true
|
|
|
|
|
2017-10-02 11:23:14 +00:00
|
|
|
auto_reconnect_after = 180
|
2016-01-09 12:23:11 +00:00
|
|
|
last_channels = []
|
|
|
|
|
|
|
|
loop do
|
|
|
|
logger.debug 'stream controll loop'
|
2017-06-01 06:23:53 +00:00
|
|
|
|
2016-01-09 12:23:11 +00:00
|
|
|
current_channels = []
|
|
|
|
channels = Channel.where('active = ? AND area LIKE ?', true, '%::Account')
|
2017-10-01 12:25:52 +00:00
|
|
|
channels.each do |channel|
|
2017-10-02 13:10:07 +00:00
|
|
|
adapter = channel.options[:adapter]
|
|
|
|
next if adapter.blank?
|
2017-10-02 11:23:14 +00:00
|
|
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
|
|
|
next if !driver_class.respond_to?(:streamable?)
|
|
|
|
next if !driver_class.streamable?
|
2017-06-01 06:23:53 +00:00
|
|
|
channel_id = channel.id.to_s
|
2017-10-02 11:23:14 +00:00
|
|
|
|
|
|
|
if @@channel_stream[channel_id].blank? && @@channel_stream_started_till_at[channel_id] && @@channel_stream_started_till_at[channel_id] > Time.zone.now - 65.seconds
|
|
|
|
logger.info "skipp channel (#{channel_id}) for streaming, already tried to connect or connection was active within the last minute"
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
2017-06-01 06:23:53 +00:00
|
|
|
current_channels.push channel_id
|
|
|
|
|
2017-10-02 11:23:14 +00:00
|
|
|
# exit it channel has changed or connection is older then 180 minutes
|
2017-06-01 06:23:53 +00:00
|
|
|
if @@channel_stream[channel_id]
|
|
|
|
if @@channel_stream[channel_id][:updated_at] != channel.updated_at
|
|
|
|
logger.info "channel (#{channel.id}) has changed, stop thread"
|
|
|
|
@@channel_stream[channel_id][:thread].exit
|
|
|
|
@@channel_stream[channel_id][:thread].join
|
|
|
|
@@channel_stream[channel_id][:stream_instance].disconnect
|
|
|
|
@@channel_stream[channel_id] = false
|
2017-10-02 11:23:14 +00:00
|
|
|
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
2017-06-01 06:23:53 +00:00
|
|
|
elsif @@channel_stream[channel_id][:started_at] && @@channel_stream[channel_id][:started_at] < Time.zone.now - auto_reconnect_after.minutes
|
|
|
|
logger.info "channel (#{channel.id}) reconnect - thread is older then #{auto_reconnect_after} minutes, restart thread"
|
|
|
|
@@channel_stream[channel_id][:thread].exit
|
|
|
|
@@channel_stream[channel_id][:thread].join
|
|
|
|
@@channel_stream[channel_id][:stream_instance].disconnect
|
|
|
|
@@channel_stream[channel_id] = false
|
2017-10-02 11:23:14 +00:00
|
|
|
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
2017-06-01 06:23:53 +00:00
|
|
|
end
|
2016-01-09 12:23:11 +00:00
|
|
|
end
|
|
|
|
|
2017-06-01 06:23:53 +00:00
|
|
|
#logger.debug "thread for channel (#{channel.id}) already running" if channel_stream
|
|
|
|
next if @@channel_stream[channel_id]
|
2016-01-09 12:23:11 +00:00
|
|
|
|
2017-06-01 06:23:53 +00:00
|
|
|
@@channel_stream[channel_id] = {
|
|
|
|
updated_at: channel.updated_at,
|
|
|
|
started_at: Time.zone.now,
|
2016-01-09 12:23:11 +00:00
|
|
|
}
|
|
|
|
|
2016-11-03 23:09:05 +00:00
|
|
|
# start channels with delay
|
|
|
|
sleep @@channel_stream.count
|
|
|
|
|
2016-01-09 12:23:11 +00:00
|
|
|
# start threads for each channel
|
2017-10-01 12:25:52 +00:00
|
|
|
@@channel_stream[channel_id][:thread] = Thread.new do
|
2016-01-14 07:54:06 +00:00
|
|
|
begin
|
2016-11-03 23:09:05 +00:00
|
|
|
logger.info "Started stream channel for '#{channel.id}' (#{channel.area})..."
|
2017-10-02 11:23:14 +00:00
|
|
|
channel.status_in = 'ok'
|
|
|
|
channel.last_log_in = ''
|
|
|
|
channel.save!
|
|
|
|
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
2017-06-01 06:23:53 +00:00
|
|
|
@@channel_stream[channel_id] ||= {}
|
|
|
|
@@channel_stream[channel_id][:stream_instance] = channel.stream_instance
|
|
|
|
@@channel_stream[channel_id][:stream_instance].stream
|
|
|
|
@@channel_stream[channel_id][:stream_instance].disconnect
|
|
|
|
@@channel_stream[channel_id] = false
|
2017-10-02 11:23:14 +00:00
|
|
|
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
|
|
|
logger.info " ...stopped stream thread for '#{channel.id}'"
|
2016-01-14 07:54:06 +00:00
|
|
|
rescue => e
|
2017-10-02 11:23:14 +00:00
|
|
|
error = "Can't use stream for channel (#{channel.id}): #{e.inspect}"
|
2016-01-14 07:54:06 +00:00
|
|
|
logger.error error
|
|
|
|
logger.error e.backtrace
|
|
|
|
channel.status_in = 'error'
|
|
|
|
channel.last_log_in = error
|
2017-10-02 11:23:14 +00:00
|
|
|
channel.save!
|
2017-06-01 06:23:53 +00:00
|
|
|
@@channel_stream[channel_id] = false
|
2016-01-14 07:54:06 +00:00
|
|
|
end
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
|
|
|
end
|
2016-01-09 12:23:11 +00:00
|
|
|
|
|
|
|
# cleanup deleted channels
|
2017-10-01 12:25:52 +00:00
|
|
|
last_channels.each do |channel_id|
|
2017-10-02 11:23:14 +00:00
|
|
|
next if @@channel_stream[channel_id].blank?
|
2016-01-09 12:23:11 +00:00
|
|
|
next if current_channels.include?(channel_id)
|
2017-10-02 11:23:14 +00:00
|
|
|
logger.info "channel (#{channel_id}) not longer active, stop stream thread"
|
|
|
|
@@channel_stream[channel_id][:thread].exit
|
|
|
|
@@channel_stream[channel_id][:thread].join
|
|
|
|
@@channel_stream[channel_id][:stream_instance].disconnect
|
|
|
|
@@channel_stream[channel_id] = false
|
|
|
|
@@channel_stream_started_till_at[channel_id] = Time.zone.now
|
2017-10-01 12:25:52 +00:00
|
|
|
end
|
2017-10-02 11:23:14 +00:00
|
|
|
|
2016-01-09 12:23:11 +00:00
|
|
|
last_channels = current_channels
|
|
|
|
|
2017-06-01 06:23:53 +00:00
|
|
|
sleep 20
|
2016-01-09 12:23:11 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
2015-08-28 00:53:14 +00:00
|
|
|
send via account
|
|
|
|
|
|
|
|
channel = Channel.where(area: 'Email::Account').first
|
|
|
|
channel.deliver(mail_params, notification)
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def deliver(mail_params, notification = false)
|
|
|
|
|
|
|
|
adapter = options[:adapter]
|
|
|
|
adapter_options = options
|
2015-08-30 11:58:05 +00:00
|
|
|
if options[:outbound] && options[:outbound][:adapter]
|
2015-08-28 00:53:14 +00:00
|
|
|
adapter = options[:outbound][:adapter]
|
|
|
|
adapter_options = options[:outbound][:options]
|
|
|
|
end
|
|
|
|
|
2015-08-28 01:08:55 +00:00
|
|
|
result = nil
|
|
|
|
|
2015-08-28 00:53:14 +00:00
|
|
|
begin
|
|
|
|
|
|
|
|
# we need to require each channel backend individually otherwise we get a
|
|
|
|
# 'warning: toplevel constant Twitter referenced by Channel::Driver::Twitter' error e.g.
|
|
|
|
# so we have to convert the channel name to the filename via Rails String.underscore
|
|
|
|
# http://stem.ps/rails/2015/01/25/ruby-gotcha-toplevel-constant-referenced-by.html
|
|
|
|
require "channel/driver/#{adapter.to_filename}"
|
|
|
|
|
|
|
|
driver_class = Object.const_get("Channel::Driver::#{adapter.to_classname}")
|
|
|
|
driver_instance = driver_class.new
|
2015-08-28 01:08:55 +00:00
|
|
|
result = driver_instance.send(adapter_options, mail_params, notification)
|
2015-08-28 00:53:14 +00:00
|
|
|
self.status_out = 'ok'
|
|
|
|
self.last_log_out = ''
|
2017-10-02 11:23:14 +00:00
|
|
|
save!
|
2015-08-28 00:53:14 +00:00
|
|
|
rescue => e
|
|
|
|
error = "Can't use Channel::Driver::#{adapter.to_classname}: #{e.inspect}"
|
|
|
|
logger.error error
|
|
|
|
logger.error e.backtrace
|
|
|
|
self.status_out = 'error'
|
|
|
|
self.last_log_out = error
|
2017-10-02 11:23:14 +00:00
|
|
|
save!
|
2016-06-25 06:54:25 +00:00
|
|
|
raise error
|
2015-08-28 00:53:14 +00:00
|
|
|
end
|
2015-08-28 01:08:55 +00:00
|
|
|
result
|
2015-08-28 00:53:14 +00:00
|
|
|
end
|
|
|
|
|
2015-08-28 08:19:27 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def email_address_check
|
|
|
|
|
|
|
|
# reset non existing channel_ids
|
|
|
|
EmailAddress.channel_cleanup
|
|
|
|
end
|
|
|
|
|
2013-06-12 15:59:58 +00:00
|
|
|
end
|