heathrow 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +58 -0
- data/README.md +205 -0
- data/bin/heathrow +42 -0
- data/bin/heathrowd +283 -0
- data/docs/ARCHITECTURE.md +1172 -0
- data/docs/DATABASE_SCHEMA.md +685 -0
- data/docs/DEVELOPMENT_WORKFLOW.md +867 -0
- data/docs/DISCORD_SETUP.md +142 -0
- data/docs/GMAIL_OAUTH_SETUP.md +120 -0
- data/docs/PLUGIN_SYSTEM.md +1370 -0
- data/docs/PROJECT_PLAN.md +1022 -0
- data/docs/README.md +417 -0
- data/docs/REDDIT_SETUP.md +174 -0
- data/docs/REPLY_FORWARD.md +182 -0
- data/docs/WHATSAPP_TELEGRAM_SETUP.md +306 -0
- data/heathrow.gemspec +34 -0
- data/heathrowd.service +21 -0
- data/img/heathrow.svg +95 -0
- data/img/rss_threaded.png +0 -0
- data/img/sources.png +0 -0
- data/lib/heathrow/address_book.rb +42 -0
- data/lib/heathrow/config.rb +332 -0
- data/lib/heathrow/database.rb +731 -0
- data/lib/heathrow/database_new.rb +392 -0
- data/lib/heathrow/event_bus.rb +175 -0
- data/lib/heathrow/logger.rb +122 -0
- data/lib/heathrow/message.rb +176 -0
- data/lib/heathrow/message_composer.rb +399 -0
- data/lib/heathrow/message_organizer.rb +774 -0
- data/lib/heathrow/migrations/001_initial_schema.rb +248 -0
- data/lib/heathrow/notmuch.rb +45 -0
- data/lib/heathrow/oauth2_smtp.rb +254 -0
- data/lib/heathrow/plugin/base.rb +212 -0
- data/lib/heathrow/plugin_manager.rb +141 -0
- data/lib/heathrow/poller.rb +93 -0
- data/lib/heathrow/smtp_sender.rb +204 -0
- data/lib/heathrow/source.rb +39 -0
- data/lib/heathrow/sources/base.rb +74 -0
- data/lib/heathrow/sources/discord.rb +357 -0
- data/lib/heathrow/sources/gmail.rb +294 -0
- data/lib/heathrow/sources/imap.rb +198 -0
- data/lib/heathrow/sources/instagram.rb +307 -0
- data/lib/heathrow/sources/instagram_fetch.py +101 -0
- data/lib/heathrow/sources/instagram_send.py +55 -0
- data/lib/heathrow/sources/instagram_send_marionette.py +104 -0
- data/lib/heathrow/sources/maildir.rb +606 -0
- data/lib/heathrow/sources/messenger.rb +212 -0
- data/lib/heathrow/sources/messenger_fetch.js +297 -0
- data/lib/heathrow/sources/messenger_fetch_marionette.py +138 -0
- data/lib/heathrow/sources/messenger_send.js +32 -0
- data/lib/heathrow/sources/messenger_send.py +100 -0
- data/lib/heathrow/sources/reddit.rb +461 -0
- data/lib/heathrow/sources/rss.rb +299 -0
- data/lib/heathrow/sources/slack.rb +375 -0
- data/lib/heathrow/sources/source_manager.rb +328 -0
- data/lib/heathrow/sources/telegram.rb +498 -0
- data/lib/heathrow/sources/webpage.rb +207 -0
- data/lib/heathrow/sources/weechat.rb +479 -0
- data/lib/heathrow/sources/whatsapp.rb +474 -0
- data/lib/heathrow/ui/application.rb +8098 -0
- data/lib/heathrow/ui/navigation.rb +8 -0
- data/lib/heathrow/ui/panes.rb +8 -0
- data/lib/heathrow/ui/source_wizard.rb +567 -0
- data/lib/heathrow/ui/threaded_view.rb +780 -0
- data/lib/heathrow/ui/views.rb +8 -0
- data/lib/heathrow/version.rb +3 -0
- data/lib/heathrow/wizards/discord_wizard.rb +193 -0
- data/lib/heathrow/wizards/slack_wizard.rb +140 -0
- data/lib/heathrow.rb +55 -0
- metadata +147 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Heathrow
|
|
2
|
+
module Sources
|
|
3
|
+
class Base
|
|
4
|
+
attr_reader :name, :type, :config, :db
|
|
5
|
+
|
|
6
|
+
def initialize(name, config, db)
|
|
7
|
+
@name = name
|
|
8
|
+
@config = config
|
|
9
|
+
@db = db
|
|
10
|
+
@type = self.class.name.split('::').last.downcase
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fetch
|
|
14
|
+
raise NotImplementedError, "#{self.class} must implement #fetch"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def poll_interval
|
|
18
|
+
@config['poll_interval'] || 300
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def enabled?
|
|
22
|
+
@config['enabled'] != false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def last_fetch
|
|
26
|
+
@config['last_fetch']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def update_last_fetch(time = Time.now)
|
|
30
|
+
@config['last_fetch'] = time.to_i
|
|
31
|
+
save_config
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def save_config
|
|
35
|
+
source = @db.get_source_by_name(@name)
|
|
36
|
+
if source
|
|
37
|
+
sid = source['id'] || source[:id]
|
|
38
|
+
@db.update_source(sid, config: @config.to_json) if sid
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
def store_message(external_id, title, content, timestamp = Time.now, metadata = {})
|
|
45
|
+
source = @db.get_source_by_name(@name)
|
|
46
|
+
return unless source
|
|
47
|
+
|
|
48
|
+
# Check if message already exists
|
|
49
|
+
existing = @db.get_messages(
|
|
50
|
+
source_id: source['id']
|
|
51
|
+
).find { |m| m['external_id'] == external_id }
|
|
52
|
+
|
|
53
|
+
return if existing
|
|
54
|
+
|
|
55
|
+
# Store new message - insert_message expects an array in specific order
|
|
56
|
+
message_data = [
|
|
57
|
+
source['id'], # source_id
|
|
58
|
+
source['type'], # source_type
|
|
59
|
+
external_id, # external_id
|
|
60
|
+
metadata[:author], # sender
|
|
61
|
+
nil, # recipient
|
|
62
|
+
title, # subject
|
|
63
|
+
content, # content
|
|
64
|
+
metadata.to_json, # raw_data
|
|
65
|
+
nil, # attachments
|
|
66
|
+
timestamp, # timestamp
|
|
67
|
+
0 # is_read
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
@db.insert_message(message_data)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
module Heathrow
|
|
10
|
+
module Sources
|
|
11
|
+
class Discord
|
|
12
|
+
API_BASE = 'https://discord.com/api/v10'
|
|
13
|
+
|
|
14
|
+
def initialize(source)
|
|
15
|
+
@source = source
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def name
|
|
19
|
+
'Discord'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def description
|
|
23
|
+
'Fetch messages from Discord servers and DMs'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def fetch_messages
|
|
27
|
+
token = @source.config['token']
|
|
28
|
+
is_bot = @source.config['is_bot'] || false
|
|
29
|
+
channels = @source.config['channels'] || []
|
|
30
|
+
guilds = @source.config['guilds'] || []
|
|
31
|
+
fetch_limit = @source.config['fetch_limit'] || 50
|
|
32
|
+
|
|
33
|
+
messages = []
|
|
34
|
+
|
|
35
|
+
# Get state file to track last message IDs per channel
|
|
36
|
+
heathrow_home = File.expand_path('~/.heathrow')
|
|
37
|
+
state_file = File.join(heathrow_home, 'state', "discord_#{@source.id}.json")
|
|
38
|
+
FileUtils.mkdir_p(File.dirname(state_file))
|
|
39
|
+
|
|
40
|
+
last_messages = if File.exist?(state_file)
|
|
41
|
+
JSON.parse(File.read(state_file))
|
|
42
|
+
else
|
|
43
|
+
{}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
# If specific channels are provided, fetch from those
|
|
48
|
+
if channels && !channels.empty?
|
|
49
|
+
channels_list = channels.is_a?(String) ? channels.split(',').map(&:strip) : channels
|
|
50
|
+
channels_list.each do |channel_id|
|
|
51
|
+
# Get channel info to determine if it's a guild channel or DM
|
|
52
|
+
channel_info = fetch_channel_info(token, channel_id, is_bot)
|
|
53
|
+
|
|
54
|
+
if channel_info
|
|
55
|
+
guild_id = channel_info['guild_id']
|
|
56
|
+
channel_name = channel_info['name'] || channel_id
|
|
57
|
+
|
|
58
|
+
# If it's a guild channel, get guild info
|
|
59
|
+
if guild_id
|
|
60
|
+
guild_info = fetch_guild_info(token, guild_id, is_bot)
|
|
61
|
+
guild_name = guild_info ? guild_info['name'] : "Server"
|
|
62
|
+
channel_messages = fetch_channel_messages(token, channel_id, last_messages[channel_id], fetch_limit, is_bot, guild_name, channel_name, guild_id)
|
|
63
|
+
else
|
|
64
|
+
# It's a DM channel
|
|
65
|
+
channel_messages = fetch_channel_messages(token, channel_id, last_messages[channel_id], fetch_limit, is_bot)
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
# Fallback if we can't get channel info
|
|
69
|
+
channel_messages = fetch_channel_messages(token, channel_id, last_messages[channel_id], fetch_limit, is_bot)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
messages.concat(channel_messages)
|
|
73
|
+
|
|
74
|
+
# Update last message ID for this channel
|
|
75
|
+
if channel_messages.any?
|
|
76
|
+
last_messages[channel_id] = channel_messages.first[:external_id].split('_').last
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# If guilds are provided, fetch all channels from those guilds
|
|
82
|
+
if guilds && !guilds.empty?
|
|
83
|
+
guilds_list = guilds.is_a?(String) ? guilds.split(',').map(&:strip) : guilds
|
|
84
|
+
guilds_list.each do |guild_id|
|
|
85
|
+
# Get guild info for proper naming
|
|
86
|
+
guild_info = fetch_guild_info(token, guild_id, is_bot)
|
|
87
|
+
guild_name = guild_info ? guild_info['name'] : "Guild #{guild_id}"
|
|
88
|
+
|
|
89
|
+
guild_channels = fetch_guild_channels(token, guild_id, is_bot)
|
|
90
|
+
guild_channels.each do |channel|
|
|
91
|
+
next unless channel['type'] == 0 # Only text channels
|
|
92
|
+
|
|
93
|
+
channel_id = channel['id']
|
|
94
|
+
channel_messages = fetch_channel_messages(token, channel_id, last_messages[channel_id], fetch_limit, is_bot, guild_name, channel['name'], guild_id)
|
|
95
|
+
messages.concat(channel_messages)
|
|
96
|
+
|
|
97
|
+
# Update last message ID for this channel
|
|
98
|
+
if channel_messages.any?
|
|
99
|
+
last_messages[channel_id] = channel_messages.first[:external_id].split('_').last
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# If neither channels nor guilds specified, try to get DMs
|
|
106
|
+
if channels.empty? && guilds.empty?
|
|
107
|
+
dm_channels = fetch_dm_channels(token, is_bot)
|
|
108
|
+
dm_channels.each do |channel|
|
|
109
|
+
channel_id = channel['id']
|
|
110
|
+
channel_messages = fetch_channel_messages(token, channel_id, last_messages[channel_id], fetch_limit, is_bot)
|
|
111
|
+
messages.concat(channel_messages)
|
|
112
|
+
|
|
113
|
+
# Update last message ID for this channel
|
|
114
|
+
if channel_messages.any?
|
|
115
|
+
last_messages[channel_id] = channel_messages.first[:external_id].split('_').last
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Save state
|
|
121
|
+
File.write(state_file, JSON.generate(last_messages))
|
|
122
|
+
|
|
123
|
+
rescue => e
|
|
124
|
+
return [{
|
|
125
|
+
source_id: @source.id,
|
|
126
|
+
source_type: 'discord',
|
|
127
|
+
sender: 'Discord',
|
|
128
|
+
subject: 'Error',
|
|
129
|
+
content: "Failed to fetch messages: #{e.message}",
|
|
130
|
+
timestamp: Time.now.to_s,
|
|
131
|
+
is_read: 0
|
|
132
|
+
}]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
messages
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_connection
|
|
139
|
+
config = @source.config.is_a?(String) ? JSON.parse(@source.config) : @source.config
|
|
140
|
+
token = config['token']
|
|
141
|
+
is_bot = config['is_bot'] != false
|
|
142
|
+
|
|
143
|
+
begin
|
|
144
|
+
# Try to get user info
|
|
145
|
+
uri = URI("#{API_BASE}/users/@me")
|
|
146
|
+
request = Net::HTTP::Get.new(uri)
|
|
147
|
+
request['Authorization'] = is_bot ? "Bot #{token}" : token
|
|
148
|
+
|
|
149
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
150
|
+
http.request(request)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
154
|
+
user_data = JSON.parse(response.body)
|
|
155
|
+
username = user_data['username']
|
|
156
|
+
discriminator = user_data['discriminator']
|
|
157
|
+
{ success: true, message: "Connected as #{username}##{discriminator}" }
|
|
158
|
+
else
|
|
159
|
+
{ success: false, message: "Failed to connect: #{response.code} #{response.message}" }
|
|
160
|
+
end
|
|
161
|
+
rescue => e
|
|
162
|
+
{ success: false, message: "Connection test failed: #{e.message}" }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def can_reply?
|
|
167
|
+
true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def send_message(to, subject, body, in_reply_to = nil)
|
|
171
|
+
config = @source.config.is_a?(String) ? JSON.parse(@source.config) : @source.config
|
|
172
|
+
token = config['token']
|
|
173
|
+
is_bot = config['is_bot'] != false
|
|
174
|
+
|
|
175
|
+
# Debug log
|
|
176
|
+
File.open('/tmp/heathrow_debug.log', 'a') do |f|
|
|
177
|
+
f.puts "\n=== DISCORD SEND MESSAGE #{Time.now} ==="
|
|
178
|
+
f.puts "To: #{to.inspect}"
|
|
179
|
+
f.puts "Subject: #{subject.inspect}"
|
|
180
|
+
f.puts "Body length: #{body.length}"
|
|
181
|
+
f.puts "In reply to: #{in_reply_to.inspect}"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Parse the recipient - could be channel ID or username
|
|
185
|
+
channel_id = if to =~ /^\d+$/
|
|
186
|
+
to # Already a channel ID
|
|
187
|
+
else
|
|
188
|
+
# Try to extract channel ID from a formatted string like "#general (123456789)"
|
|
189
|
+
if to =~ /\((\d+)\)/
|
|
190
|
+
$1
|
|
191
|
+
else
|
|
192
|
+
return { success: false, message: "Discord: Need channel ID, got '#{to}'. Check the To: field in editor." }
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Build the message
|
|
197
|
+
message_data = {
|
|
198
|
+
content: body
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# If replying, add reference
|
|
202
|
+
if in_reply_to
|
|
203
|
+
message_data[:message_reference] = {
|
|
204
|
+
message_id: in_reply_to
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Send the message
|
|
209
|
+
uri = URI("#{API_BASE}/channels/#{channel_id}/messages")
|
|
210
|
+
request = Net::HTTP::Post.new(uri)
|
|
211
|
+
request['Authorization'] = is_bot ? "Bot #{token}" : token
|
|
212
|
+
request['Content-Type'] = 'application/json'
|
|
213
|
+
request.body = message_data.to_json
|
|
214
|
+
|
|
215
|
+
begin
|
|
216
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
217
|
+
http.request(request)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
if response.is_a?(Net::HTTPSuccess)
|
|
221
|
+
{ success: true, message: "Message sent to Discord" }
|
|
222
|
+
else
|
|
223
|
+
error_data = JSON.parse(response.body) rescue {}
|
|
224
|
+
{ success: false, message: "Failed to send: #{error_data['message'] || response.message}" }
|
|
225
|
+
end
|
|
226
|
+
rescue => e
|
|
227
|
+
{ success: false, message: "Send failed: #{e.message}" }
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
def fetch_channel_messages(token, channel_id, last_message_id, limit, is_bot, guild_name = nil, channel_name = nil, guild_id = nil)
|
|
234
|
+
messages = []
|
|
235
|
+
|
|
236
|
+
uri = URI("#{API_BASE}/channels/#{channel_id}/messages")
|
|
237
|
+
params = { limit: [limit, 100].min }
|
|
238
|
+
params[:after] = last_message_id if last_message_id
|
|
239
|
+
uri.query = URI.encode_www_form(params)
|
|
240
|
+
|
|
241
|
+
response = make_request(uri, token, is_bot)
|
|
242
|
+
return messages unless response.is_a?(Net::HTTPSuccess)
|
|
243
|
+
|
|
244
|
+
channel_data = JSON.parse(response.body)
|
|
245
|
+
channel_data.reverse.each do |msg| # Reverse to get oldest first
|
|
246
|
+
# Skip system messages
|
|
247
|
+
next if msg['type'] != 0 && msg['type'] != 19 # 0 = normal, 19 = reply
|
|
248
|
+
|
|
249
|
+
author = msg['author']
|
|
250
|
+
sender = author['username']
|
|
251
|
+
sender += " (BOT)" if author['bot']
|
|
252
|
+
|
|
253
|
+
# Format channel display name
|
|
254
|
+
display_channel = channel_name || msg['channel_name'] || channel_id
|
|
255
|
+
|
|
256
|
+
# Determine if this is a DM based on whether we have guild info
|
|
257
|
+
is_dm = guild_name.nil? || guild_name.empty?
|
|
258
|
+
|
|
259
|
+
# Format content
|
|
260
|
+
content = msg['content']
|
|
261
|
+
|
|
262
|
+
# Add attachment info
|
|
263
|
+
if msg['attachments'] && !msg['attachments'].empty?
|
|
264
|
+
attachments = msg['attachments'].map { |a| a['filename'] }.join(', ')
|
|
265
|
+
content += "\n[Attachments: #{attachments}]"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Add embed info
|
|
269
|
+
if msg['embeds'] && !msg['embeds'].empty?
|
|
270
|
+
content += "\n[Contains #{msg['embeds'].length} embed(s)]"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Format recipient based on whether it's a guild channel or DM
|
|
274
|
+
recipient_display = if guild_name && !is_dm
|
|
275
|
+
"#{guild_name} ##{display_channel}" # Server #channel format with space
|
|
276
|
+
elsif is_dm
|
|
277
|
+
"DM" # Mark as direct message
|
|
278
|
+
else
|
|
279
|
+
display_channel
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Add guild_id to the raw message data if we have it
|
|
283
|
+
msg_with_guild = msg.dup
|
|
284
|
+
msg_with_guild['guild_id'] = guild_id if guild_id
|
|
285
|
+
|
|
286
|
+
message = {
|
|
287
|
+
source_id: @source.id,
|
|
288
|
+
source_type: 'discord',
|
|
289
|
+
external_id: "discord_#{channel_id}_#{msg['id']}",
|
|
290
|
+
sender: sender,
|
|
291
|
+
recipient: recipient_display,
|
|
292
|
+
subject: recipient_display,
|
|
293
|
+
content: content,
|
|
294
|
+
raw_data: msg_with_guild.to_json,
|
|
295
|
+
attachments: msg['attachments'] ? msg['attachments'].to_json : nil,
|
|
296
|
+
timestamp: Time.parse(msg['timestamp']),
|
|
297
|
+
is_read: 0,
|
|
298
|
+
metadata: {
|
|
299
|
+
guild_name: guild_name,
|
|
300
|
+
channel_name: display_channel,
|
|
301
|
+
channel_id: channel_id,
|
|
302
|
+
is_dm: is_dm
|
|
303
|
+
}.to_json
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
messages << message
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
messages
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def fetch_channel_info(token, channel_id, is_bot)
|
|
313
|
+
uri = URI("#{API_BASE}/channels/#{channel_id}")
|
|
314
|
+
response = make_request(uri, token, is_bot)
|
|
315
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
316
|
+
|
|
317
|
+
JSON.parse(response.body)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def fetch_guild_info(token, guild_id, is_bot)
|
|
321
|
+
uri = URI("#{API_BASE}/guilds/#{guild_id}")
|
|
322
|
+
response = make_request(uri, token, is_bot)
|
|
323
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
324
|
+
|
|
325
|
+
JSON.parse(response.body)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def fetch_guild_channels(token, guild_id, is_bot)
|
|
329
|
+
uri = URI("#{API_BASE}/guilds/#{guild_id}/channels")
|
|
330
|
+
response = make_request(uri, token, is_bot)
|
|
331
|
+
return [] unless response.is_a?(Net::HTTPSuccess)
|
|
332
|
+
|
|
333
|
+
JSON.parse(response.body)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def fetch_dm_channels(token, is_bot)
|
|
337
|
+
uri = URI("#{API_BASE}/users/@me/channels")
|
|
338
|
+
response = make_request(uri, token, is_bot)
|
|
339
|
+
return [] unless response.is_a?(Net::HTTPSuccess)
|
|
340
|
+
|
|
341
|
+
JSON.parse(response.body)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def make_request(uri, token, is_bot)
|
|
345
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
346
|
+
http.use_ssl = true
|
|
347
|
+
|
|
348
|
+
request = Net::HTTP::Get.new(uri)
|
|
349
|
+
auth_header = is_bot ? "Bot #{token}" : token
|
|
350
|
+
request['Authorization'] = auth_header
|
|
351
|
+
request['User-Agent'] = 'Heathrow/1.0'
|
|
352
|
+
|
|
353
|
+
http.request(request)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|