Class: PecRuby::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/pec_ruby/client.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host:, username:, password:, ssl: true) ⇒ Client

Returns a new instance of Client.



10
11
12
13
14
15
16
17
# File 'lib/pec_ruby/client.rb', line 10

def initialize(host:, username:, password:, ssl: true)
  @host = host
  @username = username
  @password = password
  @ssl = ssl
  @imap = nil
  @current_folder = nil
end

Instance Attribute Details

#current_folderObject (readonly)

Returns the value of attribute current_folder.



8
9
10
# File 'lib/pec_ruby/client.rb', line 8

def current_folder
  @current_folder
end

#hostObject (readonly)

Returns the value of attribute host.



8
9
10
# File 'lib/pec_ruby/client.rb', line 8

def host
  @host
end

#imapObject (readonly)

Returns the value of attribute imap.



8
9
10
# File 'lib/pec_ruby/client.rb', line 8

def imap
  @imap
end

#usernameObject (readonly)

Returns the value of attribute username.



8
9
10
# File 'lib/pec_ruby/client.rb', line 8

def username
  @username
end

Instance Method Details

#available_foldersObject

Get the list of available folders in the mailbox

Raises:



53
54
55
56
57
# File 'lib/pec_ruby/client.rb', line 53

def available_folders
  raise ConnectionError, 'Not connected' unless connected?

  @imap.list('', '*').map(&:name)
end

#connectObject



19
20
21
22
23
24
25
26
# File 'lib/pec_ruby/client.rb', line 19

def connect
  @imap = Net::IMAP.new(@host, ssl: @ssl)
  authenticate
  select_inbox
  self
rescue Net::IMAP::Error => e
  raise ConnectionError, "Failed to connect to #{@host}: #{e.message}"
end

#connected?Boolean

Returns:

  • (Boolean)


46
47
48
49
50
# File 'lib/pec_ruby/client.rb', line 46

def connected?
  return false unless @imap

  !@imap.disconnected?
end

#disconnectObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/pec_ruby/client.rb', line 28

def disconnect
  return unless @imap

  begin
    @imap.logout if @imap && !@imap.disconnected?
  rescue StandardError => e
    # Ignore logout errors if connection is already closed
  end

  begin
    @imap.disconnect if @imap && !@imap.disconnected?
  rescue StandardError => e
    # Ignore disconnect errors if connection is already closed
  end

  @imap = nil
end

#fetch_body_part(uid, part_id) ⇒ Object

Internal method to fetch message body parts



135
136
137
# File 'lib/pec_ruby/client.rb', line 135

def fetch_body_part(uid, part_id)
  @imap.uid_fetch(uid, "BODY[#{part_id}]")[0].attr["BODY[#{part_id}]"]
end

#fetch_messages_by_sequence(sequence_numbers) ⇒ Object

Internal method to fetch messages by sequence number



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/pec_ruby/client.rb', line 116

def fetch_messages_by_sequence(sequence_numbers)
  return [] if sequence_numbers.empty?

  # Fetch messages in batches to avoid memory issues
  batch_size = 50
  all_messages = []

  sequence_numbers.each_slice(batch_size) do |seq_batch|
    fetch_data = @imap.fetch(seq_batch, %w[UID ENVELOPE BODYSTRUCTURE])
    next if fetch_data.nil?

    batch_messages = fetch_data.map { |data| Message.new(self, data) }
    all_messages.concat(batch_messages)
  end

  all_messages
end

#message(uid) ⇒ Object

Get a specific message by UID

Raises:



105
106
107
108
109
110
111
112
# File 'lib/pec_ruby/client.rb', line 105

def message(uid)
  raise ConnectionError, 'Not connected' unless connected?

  fetch_data = @imap.uid_fetch(uid, %w[UID ENVELOPE BODYSTRUCTURE])
  return nil if fetch_data.nil? || fetch_data.empty?

  Message.new(self, fetch_data.first)
end

#messages(limit: nil, reverse: true) ⇒ Object

Get all messages or a subset

Raises:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/pec_ruby/client.rb', line 79

def messages(limit: nil, reverse: true)
  raise ConnectionError, 'Not connected' unless connected?

  # Use search to get available sequence numbers (safer approach)
  begin
    sequence_numbers = @imap.search(['ALL'])
  rescue Net::IMAP::Error => e
    raise ConnectionError, "Failed to search messages: #{e.message}"
  end
  
  return [] if sequence_numbers.empty?

  # Sort and limit the sequence numbers
  if reverse
    sequence_numbers = sequence_numbers.sort.reverse
  else
    sequence_numbers = sequence_numbers.sort
  end
  
  # Apply limit if specified
  sequence_numbers = sequence_numbers.first(limit) if limit

  fetch_messages_by_sequence(sequence_numbers)
end

#select_folder(folder) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/pec_ruby/client.rb', line 59

def select_folder(folder)
  raise ConnectionError, 'Not connected' unless connected?

  # Check if the folder exists
  raise FolderError, "Folder '#{folder}' does not exist" unless available_folders.include?(folder)

  @imap.select(folder)
  @current_folder = folder
rescue Net::IMAP::Error => e
  raise FolderError, "Failed to select folder '#{folder}': #{e.message}"
end

#select_inboxObject



71
72
73
74
75
76
# File 'lib/pec_ruby/client.rb', line 71

def select_inbox
  select_folder('INBOX')
  @current_folder = 'INBOX'
rescue FolderError => e
  raise FolderError, "Failed to select INBOX: #{e.message}"
end