instagram_connect 0.3.9 → 0.3.10
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bea234dd4fd6e4a37c8c06f26c2b5101fc4a3b1b2037ea8c646fa6382e36555b
|
|
4
|
+
data.tar.gz: f4ead074b0eba3f7dc961bacccf661f45ad586a90661243aa465d2e79f4a4212
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c793edf0d1f244413a20cf705a277ca8f1927ceb888db77078c93813b208fdcb8f768cf3918c0d027b1ab5a607028481493c0f659ea10e9f09109ad00588752
|
|
7
|
+
data.tar.gz: 0d051c02f9538c9aaeb258e8220f15897042fb333dd63983fd6d3daa29f3dc796aff903dddad2dff4e5563a92c7b215075fb6f62e9b3cb20487fe9b33488b3fe
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.10] - 2026-07-27
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Comment history sync.** `SyncCommentsJob` walks a post's comments edge — replies expanded,
|
|
14
|
+
cursors followed — and upserts by Meta's id, so webhook-created rows are enriched, never
|
|
15
|
+
duplicated. Webhooks only cover comments made after the subscription existed; this imports
|
|
16
|
+
everything before. `SyncMediaJob`'s full walk now enqueues one comment walk per post that
|
|
17
|
+
actually has comments. Meta stays the source of truth for hidden, both directions.
|
|
18
|
+
|
|
19
|
+
|
|
9
20
|
## [0.3.9] - 2026-07-27
|
|
10
21
|
|
|
11
22
|
### Added
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module InstagramConnect
|
|
2
|
+
# Imports a post's existing comments — the ones that happened before the
|
|
3
|
+
# webhook subscription existed, which the webhook can never deliver. The
|
|
4
|
+
# comments edge is walked with cursors, replies expanded inline, and every
|
|
5
|
+
# comment upserted by Meta's id, so re-running never duplicates and a
|
|
6
|
+
# webhook-created row is simply enriched.
|
|
7
|
+
#
|
|
8
|
+
# Meta is the source of truth for hidden: a comment hidden from the app on
|
|
9
|
+
# the phone arrives hidden here, and one unhidden there un-hides here.
|
|
10
|
+
class SyncCommentsJob < ApplicationJob
|
|
11
|
+
queue_as :default
|
|
12
|
+
|
|
13
|
+
THROTTLE = 10.minutes
|
|
14
|
+
|
|
15
|
+
REPLY_FIELDS = "id,text,timestamp,username,from,parent_id,hidden,like_count".freeze
|
|
16
|
+
COMMENT_FIELDS = "#{REPLY_FIELDS},replies{#{REPLY_FIELDS}}".freeze
|
|
17
|
+
|
|
18
|
+
# One key per post: opening a busy post's comments repeatedly enqueues one
|
|
19
|
+
# import, not one per page view.
|
|
20
|
+
def self.enqueue_throttled(account, ig_media_id)
|
|
21
|
+
Rails.cache.fetch("instagram_connect:sync_comments:#{account.id}:#{ig_media_id}",
|
|
22
|
+
expires_in: THROTTLE) do
|
|
23
|
+
perform_later(account.id, ig_media_id.to_s)
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def perform(account_id, ig_media_id)
|
|
29
|
+
account = Account.find_by(id: account_id)
|
|
30
|
+
return if account.nil? || !account.active?
|
|
31
|
+
|
|
32
|
+
result = account.client.collect("/#{ig_media_id}/comments",
|
|
33
|
+
{ fields: COMMENT_FIELDS, limit: 50 })
|
|
34
|
+
unless result.success?
|
|
35
|
+
return logger.error("[instagram_connect] comment sync failed for " \
|
|
36
|
+
"account=#{account.id} media=#{ig_media_id}: #{result.error_message}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Array(result.data["data"]).each do |item|
|
|
40
|
+
upsert(account, ig_media_id, item)
|
|
41
|
+
Array(item.dig("replies", "data")).each do |reply|
|
|
42
|
+
# Meta omits parent_id inside the replies expansion — the nesting
|
|
43
|
+
# itself is the statement.
|
|
44
|
+
upsert(account, ig_media_id, reply.merge("parent_id" => reply["parent_id"] || item["id"]))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def upsert(account, ig_media_id, item)
|
|
52
|
+
comment = Comment.record(
|
|
53
|
+
account: account,
|
|
54
|
+
comment_id: item["id"],
|
|
55
|
+
media_id: ig_media_id.to_s,
|
|
56
|
+
text: item["text"],
|
|
57
|
+
from_username: item["username"] || item.dig("from", "username"),
|
|
58
|
+
parent_id: item["parent_id"]
|
|
59
|
+
)
|
|
60
|
+
comment.update!(
|
|
61
|
+
from_ig_id: item.dig("from", "id") || comment.from_ig_id,
|
|
62
|
+
commented_at: parse_time(item["timestamp"]) || comment.commented_at,
|
|
63
|
+
like_count: item["like_count"],
|
|
64
|
+
hidden_at: item["hidden"] ? (comment.hidden_at || Time.current) : nil
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_time(value)
|
|
69
|
+
Time.zone.parse(value.to_s)
|
|
70
|
+
rescue ArgumentError, TypeError
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -45,7 +45,14 @@ module InstagramConnect
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
Array(result.data["data"]).each { |item| upsert(account, item) }
|
|
48
|
-
|
|
48
|
+
if full
|
|
49
|
+
mark_removed(account, started_at)
|
|
50
|
+
# The full walk is the moment to fetch comment history too: webhooks
|
|
51
|
+
# only cover comments made after the subscription existed. One extra
|
|
52
|
+
# job per post that actually has comments — bounded by reality, and
|
|
53
|
+
# the walk is operator-triggered.
|
|
54
|
+
backfill_comments(account, result)
|
|
55
|
+
end
|
|
49
56
|
end
|
|
50
57
|
|
|
51
58
|
private
|
|
@@ -78,6 +85,14 @@ module InstagramConnect
|
|
|
78
85
|
media.update!(removed_from_instagram_at: nil) if media.removed_from_instagram_at
|
|
79
86
|
end
|
|
80
87
|
|
|
88
|
+
def backfill_comments(account, result)
|
|
89
|
+
Array(result.data["data"]).each do |item|
|
|
90
|
+
next unless item["comments_count"].to_i.positive?
|
|
91
|
+
|
|
92
|
+
SyncCommentsJob.perform_later(account.id, item["id"].to_s)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
81
96
|
def mark_removed(account, started_at)
|
|
82
97
|
marked = MediaItem.where(account_id: account.id, removed_from_instagram_at: nil)
|
|
83
98
|
.where(synced_at: ...started_at)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: instagram_connect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kshitiz Sinha
|
|
@@ -100,6 +100,7 @@ files:
|
|
|
100
100
|
- app/jobs/instagram_connect/refresh_tokens_job.rb
|
|
101
101
|
- app/jobs/instagram_connect/replay_events_job.rb
|
|
102
102
|
- app/jobs/instagram_connect/send_message_job.rb
|
|
103
|
+
- app/jobs/instagram_connect/sync_comments_job.rb
|
|
103
104
|
- app/jobs/instagram_connect/sync_conversations_job.rb
|
|
104
105
|
- app/jobs/instagram_connect/sync_media_job.rb
|
|
105
106
|
- app/jobs/instagram_connect/sync_stories_job.rb
|