Нет описания http://j1x-huginn.herokuapp.com

agent.rb 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. require 'utils'
  2. # Agent is the core class in Huginn, representing a configurable, schedulable, reactive system with memory that can
  3. # be sub-classed for many different purposes. Agents can emit Events, as well as receive them and react in many different ways.
  4. # The basic Agent API is detailed on the Huginn wiki: https://github.com/cantino/huginn/wiki/Creating-a-new-agent
  5. class Agent < ActiveRecord::Base
  6. include AssignableTypes
  7. include MarkdownClassAttributes
  8. include JSONSerializedField
  9. include RDBMSFunctions
  10. include WorkingHelpers
  11. include LiquidInterpolatable
  12. include HasGuid
  13. include LiquidDroppable
  14. include DryRunnable
  15. include SortableEvents
  16. markdown_class_attributes :description, :event_description
  17. load_types_in "Agents"
  18. SCHEDULES = %w[every_1m every_2m every_5m every_10m every_30m every_1h every_2h every_5h every_12h every_1d every_2d every_7d
  19. midnight 1am 2am 3am 4am 5am 6am 7am 8am 9am 10am 11am noon 1pm 2pm 3pm 4pm 5pm 6pm 7pm 8pm 9pm 10pm 11pm never]
  20. EVENT_RETENTION_SCHEDULES = [["Forever", 0], ['1 hour', 1.hour], ['6 hours', 6.hours], ["1 day", 1.day], *([2, 3, 4, 5, 7, 14, 21, 30, 45, 90, 180, 365].map {|n| ["#{n} days", n.days] })]
  21. attr_accessible :options, :memory, :name, :type, :schedule, :controller_ids, :control_target_ids, :disabled, :source_ids, :scenario_ids, :keep_events_for, :propagate_immediately, :drop_pending_events
  22. json_serialize :options, :memory
  23. validates_presence_of :name, :user
  24. validates_inclusion_of :keep_events_for, :in => EVENT_RETENTION_SCHEDULES.map(&:last)
  25. validate :sources_are_owned
  26. validate :controllers_are_owned
  27. validate :control_targets_are_owned
  28. validate :scenarios_are_owned
  29. validate :validate_schedule
  30. validate :validate_options
  31. after_initialize :set_default_schedule
  32. before_validation :set_default_schedule
  33. before_validation :unschedule_if_cannot_schedule
  34. before_save :unschedule_if_cannot_schedule
  35. before_create :set_last_checked_event_id
  36. after_save :possibly_update_event_expirations
  37. belongs_to :user, :inverse_of => :agents
  38. belongs_to :service, :inverse_of => :agents
  39. has_many :events, -> { order("events.id desc") }, :dependent => :delete_all, :inverse_of => :agent
  40. has_one :most_recent_event, -> { order("events.id desc") }, :inverse_of => :agent, :class_name => "Event"
  41. has_many :logs, -> { order("agent_logs.id desc") }, :dependent => :delete_all, :inverse_of => :agent, :class_name => "AgentLog"
  42. has_many :received_events, -> { order("events.id desc") }, :through => :sources, :class_name => "Event", :source => :events
  43. has_many :links_as_source, :dependent => :delete_all, :foreign_key => "source_id", :class_name => "Link", :inverse_of => :source
  44. has_many :links_as_receiver, :dependent => :delete_all, :foreign_key => "receiver_id", :class_name => "Link", :inverse_of => :receiver
  45. has_many :sources, :through => :links_as_receiver, :class_name => "Agent", :inverse_of => :receivers
  46. has_many :receivers, :through => :links_as_source, :class_name => "Agent", :inverse_of => :sources
  47. has_many :control_links_as_controller, dependent: :delete_all, foreign_key: 'controller_id', class_name: 'ControlLink', inverse_of: :controller
  48. has_many :control_links_as_control_target, dependent: :delete_all, foreign_key: 'control_target_id', class_name: 'ControlLink', inverse_of: :control_target
  49. has_many :controllers, through: :control_links_as_control_target, class_name: "Agent", inverse_of: :control_targets
  50. has_many :control_targets, through: :control_links_as_controller, class_name: "Agent", inverse_of: :controllers
  51. has_many :scenario_memberships, :dependent => :destroy, :inverse_of => :agent
  52. has_many :scenarios, :through => :scenario_memberships, :inverse_of => :agents
  53. scope :active, -> { where(disabled: false, deactivated: false) }
  54. scope :inactive, -> { where(['disabled = ? OR deactivated = ?', true, true]) }
  55. scope :of_type, lambda { |type|
  56. type = case type
  57. when Agent
  58. type.class.to_s
  59. else
  60. type.to_s
  61. end
  62. where(:type => type)
  63. }
  64. def short_type
  65. type.demodulize
  66. end
  67. def check
  68. # Implement me in your subclass of Agent.
  69. end
  70. def default_options
  71. # Implement me in your subclass of Agent.
  72. {}
  73. end
  74. def receive(events)
  75. # Implement me in your subclass of Agent.
  76. end
  77. def is_form_configurable?
  78. false
  79. end
  80. def receive_web_request(params, method, format)
  81. # Implement me in your subclass of Agent.
  82. ["not implemented", 404]
  83. end
  84. # alternate method signature for receive_web_request
  85. # def receive_web_request(request=ActionDispatch::Request.new( ... ))
  86. # end
  87. # Implement me in your subclass to decide if your Agent is working.
  88. def working?
  89. raise "Implement me in your subclass"
  90. end
  91. def build_event(event)
  92. event = events.build(event) if event.is_a?(Hash)
  93. event.agent = self
  94. event.user = user
  95. event.expires_at ||= new_event_expiration_date
  96. event
  97. end
  98. def create_event(event)
  99. if can_create_events?
  100. event = build_event(event)
  101. event.save!
  102. event
  103. else
  104. error "This Agent cannot create events!"
  105. end
  106. end
  107. def credential(name)
  108. @credential_cache ||= {}
  109. if @credential_cache.has_key?(name)
  110. @credential_cache[name]
  111. else
  112. @credential_cache[name] = user.user_credentials.where(:credential_name => name).first.try(:credential_value)
  113. end
  114. end
  115. def reload
  116. @credential_cache = {}
  117. super
  118. end
  119. def new_event_expiration_date
  120. keep_events_for > 0 ? keep_events_for.seconds.from_now : nil
  121. end
  122. def update_event_expirations!
  123. if keep_events_for == 0
  124. events.update_all :expires_at => nil
  125. else
  126. events.update_all "expires_at = " + rdbms_date_add("created_at", "SECOND", keep_events_for.to_i)
  127. end
  128. end
  129. def trigger_web_request(request)
  130. params = request.params.except(:action, :controller, :agent_id, :user_id, :format)
  131. if respond_to?(:receive_webhook)
  132. Rails.logger.warn "DEPRECATED: The .receive_webhook method is deprecated, please switch your Agent to use .receive_web_request."
  133. receive_webhook(params).tap do
  134. self.last_web_request_at = Time.now
  135. save!
  136. end
  137. else
  138. if method(:receive_web_request).arity == 1
  139. handled_request = receive_web_request(request)
  140. else
  141. handled_request = receive_web_request(params, request.method_symbol.to_s, request.format.to_s)
  142. end
  143. handled_request.tap do
  144. self.last_web_request_at = Time.now
  145. save!
  146. end
  147. end
  148. end
  149. def unavailable?
  150. disabled? || dependencies_missing?
  151. end
  152. def dependencies_missing?
  153. self.class.dependencies_missing?
  154. end
  155. def default_schedule
  156. self.class.default_schedule
  157. end
  158. def cannot_be_scheduled?
  159. self.class.cannot_be_scheduled?
  160. end
  161. def can_be_scheduled?
  162. !cannot_be_scheduled?
  163. end
  164. def cannot_receive_events?
  165. self.class.cannot_receive_events?
  166. end
  167. def can_receive_events?
  168. !cannot_receive_events?
  169. end
  170. def cannot_create_events?
  171. self.class.cannot_create_events?
  172. end
  173. def can_create_events?
  174. !cannot_create_events?
  175. end
  176. def can_control_other_agents?
  177. self.class.can_control_other_agents?
  178. end
  179. def can_dry_run?
  180. self.class.can_dry_run?
  181. end
  182. def no_bulk_receive?
  183. self.class.no_bulk_receive?
  184. end
  185. def log(message, options = {})
  186. AgentLog.log_for_agent(self, message, options)
  187. end
  188. def error(message, options = {})
  189. log(message, options.merge(:level => 4))
  190. end
  191. def delete_logs!
  192. logs.delete_all
  193. update_column :last_error_log_at, nil
  194. end
  195. def drop_pending_events
  196. false
  197. end
  198. def drop_pending_events=(bool)
  199. set_last_checked_event_id if bool
  200. end
  201. # Callbacks
  202. def set_default_schedule
  203. self.schedule = default_schedule unless schedule.present? || cannot_be_scheduled?
  204. end
  205. def unschedule_if_cannot_schedule
  206. self.schedule = nil if cannot_be_scheduled?
  207. end
  208. def set_last_checked_event_id
  209. if can_receive_events? && newest_event_id = Event.maximum(:id)
  210. self.last_checked_event_id = newest_event_id
  211. end
  212. end
  213. def possibly_update_event_expirations
  214. update_event_expirations! if keep_events_for_changed?
  215. end
  216. #Validation Methods
  217. private
  218. def sources_are_owned
  219. errors.add(:sources, "must be owned by you") unless sources.all? {|s| s.user_id == user_id }
  220. end
  221. def controllers_are_owned
  222. errors.add(:controllers, "must be owned by you") unless controllers.all? {|s| s.user_id == user_id }
  223. end
  224. def control_targets_are_owned
  225. errors.add(:control_targets, "must be owned by you") unless control_targets.all? {|s| s.user_id == user_id }
  226. end
  227. def scenarios_are_owned
  228. errors.add(:scenarios, "must be owned by you") unless scenarios.all? {|s| s.user_id == user_id }
  229. end
  230. def validate_schedule
  231. unless cannot_be_scheduled?
  232. errors.add(:schedule, "is not a valid schedule") unless SCHEDULES.include?(schedule.to_s)
  233. end
  234. end
  235. def validate_options
  236. # Implement me in your subclass to test for valid options.
  237. end
  238. # Utility Methods
  239. def boolify(option_value)
  240. case option_value
  241. when true, 'true'
  242. true
  243. when false, 'false'
  244. false
  245. else
  246. nil
  247. end
  248. end
  249. # Class Methods
  250. class << self
  251. def build_clone(original)
  252. new(original.slice(:type, :options, :schedule, :controller_ids, :control_target_ids,
  253. :source_ids, :keep_events_for, :propagate_immediately, :scenario_ids)) { |clone|
  254. # Give it a unique name
  255. 2.upto(count) do |i|
  256. name = '%s (%d)' % [original.name, i]
  257. unless exists?(name: name)
  258. clone.name = name
  259. break
  260. end
  261. end
  262. }
  263. end
  264. def cannot_be_scheduled!
  265. @cannot_be_scheduled = true
  266. end
  267. def cannot_be_scheduled?
  268. !!@cannot_be_scheduled
  269. end
  270. def default_schedule(schedule = nil)
  271. @default_schedule = schedule unless schedule.nil?
  272. @default_schedule
  273. end
  274. def cannot_create_events!
  275. @cannot_create_events = true
  276. end
  277. def cannot_create_events?
  278. !!@cannot_create_events
  279. end
  280. def cannot_receive_events!
  281. @cannot_receive_events = true
  282. end
  283. def cannot_receive_events?
  284. !!@cannot_receive_events
  285. end
  286. def can_control_other_agents?
  287. include? AgentControllerConcern
  288. end
  289. def can_dry_run!
  290. @can_dry_run = true
  291. end
  292. def can_dry_run?
  293. !!@can_dry_run
  294. end
  295. def no_bulk_receive!
  296. @no_bulk_receive = true
  297. end
  298. def no_bulk_receive?
  299. !!@no_bulk_receive
  300. end
  301. def gem_dependency_check
  302. @gem_dependencies_checked = true
  303. @gem_dependencies_met = yield
  304. end
  305. def dependencies_missing?
  306. @gem_dependencies_checked && !@gem_dependencies_met
  307. end
  308. # Find all Agents that have received Events since the last execution of this method. Update those Agents with
  309. # their new `last_checked_event_id` and queue each of the Agents to be called with #receive using `async_receive`.
  310. # This is called by bin/schedule.rb periodically.
  311. def receive!(options={})
  312. Agent.transaction do
  313. scope = Agent.
  314. select("agents.id AS receiver_agent_id, events.id AS event_id").
  315. joins("JOIN links ON (links.receiver_id = agents.id)").
  316. joins("JOIN agents AS sources ON (links.source_id = sources.id)").
  317. joins("JOIN events ON (events.agent_id = sources.id AND events.id > links.event_id_at_creation)").
  318. where("NOT agents.disabled AND NOT agents.deactivated AND (agents.last_checked_event_id IS NULL OR events.id > agents.last_checked_event_id)")
  319. if options[:only_receivers].present?
  320. scope = scope.where("agents.id in (?)", options[:only_receivers])
  321. end
  322. sql = scope.to_sql()
  323. agents_to_events = {}
  324. Agent.connection.select_rows(sql).each do |receiver_agent_id, event_id|
  325. agents_to_events[receiver_agent_id.to_i] ||= []
  326. agents_to_events[receiver_agent_id.to_i] << event_id
  327. end
  328. Agent.where(:id => agents_to_events.keys).each do |agent|
  329. event_ids = agents_to_events[agent.id].uniq
  330. agent.update_attribute :last_checked_event_id, event_ids.max
  331. if agent.no_bulk_receive?
  332. event_ids.each { |event_id| Agent.async_receive(agent.id, [event_id]) }
  333. else
  334. Agent.async_receive(agent.id, event_ids)
  335. end
  336. end
  337. {
  338. :agent_count => agents_to_events.keys.length,
  339. :event_count => agents_to_events.values.flatten.uniq.compact.length
  340. }
  341. end
  342. end
  343. # This method will enqueue an AgentReceiveJob job. It accepts Agent and Event ids instead of a literal ActiveRecord
  344. # models because it is preferable to serialize jobs with ids.
  345. def async_receive(agent_id, event_ids)
  346. AgentReceiveJob.perform_later(agent_id, event_ids)
  347. end
  348. # Given a schedule name, run `check` via `bulk_check` on all Agents with that schedule.
  349. # This is called by bin/schedule.rb for each schedule in `SCHEDULES`.
  350. def run_schedule(schedule)
  351. return if schedule == 'never'
  352. types = where(:schedule => schedule).group(:type).pluck(:type)
  353. types.each do |type|
  354. type.constantize.bulk_check(schedule)
  355. end
  356. end
  357. # Schedule `async_check`s for every Agent on the given schedule. This is normally called by `run_schedule` once
  358. # per type of agent, so you can override this to define custom bulk check behavior for your custom Agent type.
  359. def bulk_check(schedule)
  360. raise "Call #bulk_check on the appropriate subclass of Agent" if self == Agent
  361. where("NOT disabled AND NOT deactivated AND schedule = ?", schedule).pluck("agents.id").each do |agent_id|
  362. async_check(agent_id)
  363. end
  364. end
  365. # This method will enqueue an AgentCheckJob job. It accepts an Agent id instead of a literal Agent because it is
  366. # preferable to serialize job with ids, instead of with the full Agents.
  367. def async_check(agent_id)
  368. AgentCheckJob.perform_later(agent_id)
  369. end
  370. end
  371. end
  372. class AgentDrop
  373. def type
  374. @object.short_type
  375. end
  376. [
  377. :name,
  378. :type,
  379. :options,
  380. :memory,
  381. :sources,
  382. :receivers,
  383. :schedule,
  384. :controllers,
  385. :control_targets,
  386. :disabled,
  387. :keep_events_for,
  388. :propagate_immediately,
  389. ].each { |attr|
  390. define_method(attr) {
  391. @object.__send__(attr)
  392. } unless method_defined?(attr)
  393. }
  394. end