Class: Heathrow::Sources::Instagram

Inherits:
Base
  • Object
show all
Defined in:
lib/heathrow/sources/instagram.rb

Defined Under Namespace

Classes: SessionExpiredError

Constant Summary collapse

File.join(Dir.home, '.heathrow', 'cookies')
File.join(COOKIE_DIR, 'instagram.json')
INBOX_URL =
'https://www.instagram.com/api/v1/direct_v2/inbox/'
APP_ID =
'936619743392459'
REQUIRED_COOKIES =
%w[sessionid csrftoken]
OPTIONAL_COOKIES =
%w[ds_user_id ig_did mid]
SEND_SCRIPT =
File.join(__dir__, 'instagram_send_marionette.py')

Instance Attribute Summary

Attributes inherited from Base

#config, #db, #name, #type

Instance Method Summary collapse

Methods inherited from Base

#enabled?, #last_fetch, #poll_interval, #save_config, #update_last_fetch

Constructor Details

#initialize(name, config, db) ⇒ Instagram

Returns a new instance of Instagram.



18
19
20
21
# File 'lib/heathrow/sources/instagram.rb', line 18

def initialize(name, config, db)
  super
  @cookies = config['cookies'] || load_cookies
end

Instance Method Details

#fetchObject



46
47
48
49
50
51
52
53
# File 'lib/heathrow/sources/instagram.rb', line 46

def fetch
  return [] unless enabled?
  source = @db.get_source_by_name(@name)
  return [] unless source
  sync(source['id'])
  update_last_fetch
  []
end

#save_cookies(cookies = nil) ⇒ Object



60
61
62
63
64
65
66
# File 'lib/heathrow/sources/instagram.rb', line 60

def save_cookies(cookies = nil)
  cookies ||= @cookies
  Dir.mkdir(COOKIE_DIR) unless Dir.exist?(COOKIE_DIR)
  File.write(COOKIE_FILE, cookies.to_json)
  File.chmod(0600, COOKIE_FILE)
  @cookies = cookies
end

#send_message(thread_id, _subject, body) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/heathrow/sources/instagram.rb', line 70

def send_message(thread_id, _subject, body)
  return { success: false, message: "No thread ID" } unless thread_id
  return { success: false, message: "Empty message" } if body.nil? || body.strip.empty?

  result = `python3 #{Shellwords.escape(SEND_SCRIPT)} #{Shellwords.escape(thread_id)} #{Shellwords.escape(body.strip)} 2>/dev/null`

  data = JSON.parse(result) rescue nil
  if data
    data.transform_keys!(&:to_sym)
    data
  else
    { success: false, message: "Instagram send: no response from script" }
  end
rescue => e
  { success: false, message: "Instagram send error: #{e.message}" }
end

#sync(source_id) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/heathrow/sources/instagram.rb', line 23

def sync(source_id)
  return 0 unless valid_cookies?

  begin
    inbox = fetch_inbox
    return 0 unless inbox

    count = 0
    threads = inbox['inbox']&.dig('threads') || []
    threads.each do |thread|
      count += process_thread(source_id, thread)
    end
    count
  rescue SessionExpiredError => e
    STDERR.puts "Instagram session expired: #{e.message}" if ENV['DEBUG']
    save_session_error("Session expired. Please refresh cookies.")
    0
  rescue => e
    STDERR.puts "Instagram error: #{e.message}\n#{e.backtrace.first(3).join("\n")}" if ENV['DEBUG']
    0
  end
end

#valid_cookies?Boolean

Returns:

  • (Boolean)


55
56
57
58
# File 'lib/heathrow/sources/instagram.rb', line 55

def valid_cookies?
  return false unless @cookies.is_a?(Hash)
  REQUIRED_COOKIES.all? { |k| @cookies[k] && !@cookies[k].empty? }
end