imsg-grep 0.1.2-darwin

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.
data/bin/msg-info ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+ # Display detailed information about a specific message by ID from the iMessage database
3
+
4
+ require "bundler/setup"
5
+
6
+ require_relative '../lib/imsg-grep/dev/print_query'
7
+ require_relative '../lib/imsg-grep/messages'
8
+
9
+ db = Messages.init
10
+
11
+ def print_results(results2)
12
+ puts "\n#{'=' * 80}"
13
+
14
+ cols, *rows = results2
15
+ width = cols.map(&:length).max
16
+ rows.each do |row|
17
+ row.each_with_index do |val, i|
18
+ if cols[i] == "payload_json" && val
19
+ val = IO.popen("jq -C", "r+") { |jq| jq << val; jq.close_write; jq.read.chomp }
20
+ else
21
+ val = val.nil? ? "NULL" : val.to_s
22
+ val = val.gsub("\n", "\n " + ' ' * width)
23
+ end
24
+ puts "#{cols[i].ljust(width, ' ')} : #{val}"
25
+ end
26
+ puts "-" * 80
27
+ end
28
+ end
29
+
30
+ def print_message_details(db, msg_id)
31
+ id_cond = msg_id =~ /-/ ? "orig.guid" : "orig.ROWID"
32
+ print_results db.execute2(<<~SQL, msg_id)
33
+ SELECT
34
+ -- Core identifiers
35
+ m.message_id,
36
+ m.guid,
37
+ m.utc_time,
38
+ -- -- Timing
39
+ -- m.utc_time,
40
+
41
+ -- -- Chat context
42
+ -- m.chat_style,
43
+ -- m.chat_name,
44
+
45
+ -- -- Message content
46
+
47
+ m.sender_name,
48
+ m.sender_searchable,
49
+ m.recipient_searchable,
50
+ m.recipient_name,
51
+ m.recipient_names,
52
+ m.recipients_searchable,
53
+ m.member_names,
54
+ m.members_searchable,
55
+
56
+ m.has_attachments,
57
+ m.is_from_me,
58
+ orig.text as text_original,
59
+ m.text,
60
+ unarchive_string(orig.attributedBody) as text_fresh,
61
+
62
+ -- Raw message fields
63
+ orig.subject,
64
+ orig.service,
65
+ orig.account,
66
+ orig.date,
67
+ orig.date_read,
68
+ orig.date_delivered,
69
+
70
+ -- Thread/Reply
71
+ orig.thread_originator_guid,
72
+ orig.thread_originator_part,
73
+ orig.reply_to_guid,
74
+
75
+ -- Metadata
76
+ orig.version,
77
+ orig.sort_id,
78
+ orig.share_status,
79
+ orig.share_direction,
80
+ orig.group_action_type,
81
+ orig.balloon_bundle_id,
82
+ orig.destination_caller_id,
83
+ orig.associated_message_type,
84
+ orig.associated_message_guid,
85
+
86
+ -- Status flags
87
+ orig.is_delivered,
88
+ orig.is_finished,
89
+ orig.is_emote,
90
+ orig.is_delayed,
91
+ orig.is_auto_reply,
92
+ orig.is_prepared,
93
+ orig.is_read,
94
+ orig.is_system_message,
95
+ orig.is_sent,
96
+ orig.is_forward,
97
+ orig.is_service_message,
98
+ orig.is_spam,
99
+ orig.error,
100
+
101
+ -- Big Data
102
+ HEX(orig.attributedBody) as attributedBody_hex,
103
+ HEX(orig.payload_data) as payload_data_hex,
104
+ unarchive_keyed(m.payload_data) as payload_json
105
+
106
+
107
+ FROM _imsg.message orig
108
+ LEFT JOIN message_view m ON m.message_id = orig.ROWID
109
+ WHERE #{id_cond} = ?
110
+ SQL
111
+
112
+
113
+ id_cond = msg_id =~ /-/ ? "(SELECT ROWID FROM message WHERE guid = ?)" : "?"
114
+ Print.query(<<~SQL, msg_id)
115
+ SELECT a.ROWID, a.filename, a.mime_type, a.uti,
116
+ a.created_date,
117
+ a.hide_attachment,
118
+ a.total_bytes,
119
+ a.transfer_name
120
+ FROM _imsg.attachment a
121
+ JOIN _imsg.message_attachment_join maj ON a.ROWID = maj.attachment_id
122
+ JOIN _imsg.message m ON maj.message_id = m.ROWID
123
+ WHERE m.ROWID IN (#{id_cond})
124
+ SQL
125
+
126
+
127
+ end
128
+
129
+ if __FILE__ == $0 && ARGV[0]
130
+ ARGV.each do |id|
131
+ print_message_details(db, id)
132
+ end
133
+ end
data/bin/sql-shell ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # Interactive SQL shell for querying the iMessage database with readline history
3
+
4
+ require "bundler/setup"
5
+
6
+ require_relative '../lib/imsg-grep/messages'
7
+ require_relative '../lib/imsg-grep/dev/print_query'
8
+ require 'readline'
9
+ require 'fileutils'
10
+
11
+ $db = Messages.init
12
+
13
+ histfile = "#{ENV['HOME']}/.config/imsg-grep/history"
14
+ FileUtils.mkdir_p File.dirname(histfile)
15
+ Readline::HISTORY.push(*File.readlines(histfile).map(&:chomp)) if File.exist?(histfile)
16
+
17
+ while buf = Readline.readline("sql> ", true)
18
+ break if buf.strip =~ /^(exit|quit)$/i
19
+ begin
20
+ Print.query(buf)
21
+ File.write(histfile, "#{buf}\n", mode: 'a')
22
+ rescue => e
23
+ puts "Error: #{e.message}"
24
+ end
25
+ end
data/doc/HELP ADDED
@@ -0,0 +1,181 @@
1
+ Usage: PROG [options ...] [PATTERN ...]
2
+
3
+ Date filtering:
4
+ -d, --since DATE Match after DATE
5
+ -u, --until DATE Match before DATE
6
+ -Z, --utc Parse and print dates as UTC
7
+
8
+ Participant filtering:
9
+ -t, --to CONTACT Match recipients
10
+ -f, --from CONTACT Match sender
11
+ -w, --with CONTACT Match sender or recipients
12
+ -c, --chat [PATTERN] With no argument, match any group chat
13
+ Otherwise PATTERN matches chat name
14
+
15
+ Message filtering:
16
+ -M, --message PATTERN Match message text
17
+ Useful for boolean expressions: -f x --or -M y
18
+ -s, --sent Only match sent
19
+ -r, --received Only match received
20
+ -V, --service SRV Filter by messaging service
21
+ One of: any|imessage|rcs|sms (default: any)
22
+ -n, --max NUM Only print top NUM messages
23
+ -a, --all Allow calling with no conditions
24
+ (will print every message unless -n)
25
+
26
+ Link filtering:
27
+ -L, --links [PATTERN] Find links with URL matching pattern
28
+ With no argument, find all links
29
+ -Y, --youtube Find youtube links
30
+ -S, --soundcloud Find soundcloud links
31
+ -X, --twitter Find twitter links
32
+
33
+ Pattern modifiers:
34
+ -C, --smart-case Case-sensitive only when pattern has uppercase
35
+ letters (default; turn off with -i or -I)
36
+ -i, --ignore-case Case-insensitive matches
37
+ -I, --no-ignore-case Case-sensitive matches
38
+ -q, --literal Literal string match, no regex
39
+ -x, --regexp Regexp match (default; turn off with -q)
40
+ -v, --invert-match Negate next pattern
41
+ -e, --exact Next pattern must match full string
42
+ -A, --and [PATTERN] Must also appear with previous condition
43
+ -O, --or [PATTERN] Either previous or next condition may occur
44
+ Without argument, affect the following condition
45
+
46
+ Format:
47
+ --[no-]color Enable/disable color output (default: yes if tty)
48
+ -W, --no-wrap Don't wrap text
49
+ -l, --one-line "[from -> to] message", newlines stripped
50
+ With --json, disable pretty print
51
+ -U, --urls Print only URLs in place of message text
52
+ Prefer canonical URLs if available
53
+ No change when no link present
54
+ -m, --no-meta Print only text or capture
55
+ No from/to headers, link or file details
56
+ -N, --short-names Shorten participant names like "First L"
57
+ Truncate chat names if --one-line
58
+ -T, --tiny Same as --short-names --one-line --urls
59
+ -k, --count Print total messages found at the end
60
+ -o, --capture [EXPR] Print only matched string, or EXPR
61
+ -j, --json JSON output
62
+ -P, --payload Include payload in JSON
63
+ -R, -z, --reverse Reverse sort order (oldest first)
64
+
65
+ Media:
66
+ -F, --files [KIND|KEY:PATTERN]
67
+ Find messages with files of KIND
68
+ or matching pattern for KEY
69
+ KIND: any (default) or images
70
+ KEY: name, ext, mime
71
+ -b, --list-files List attached files (default in normal list)
72
+ -B, --no-list-files Do not list attached files
73
+ -p, --preview [KIND] Inline image preview (requires terminal support)
74
+ KIND is some of: images, links; both if not given
75
+ -g, --img, --images Same as --files=images --preview=images
76
+ -Q, --silence-warnings Suppress warning messages and hints
77
+
78
+ Other:
79
+ --version Print version
80
+ --reset Clear and rebuild cache
81
+ -h, --help Show this help (see more with --help)
82
+ --help-dates Show detailed DATE help
83
+
84
+ ~~LONG HELP BELOW~~
85
+
86
+ DATES
87
+
88
+ ISO8601 format (time/TZ optional): '2024-12-30', '2024-12-30 12:34'
89
+
90
+ Relative format: 4y (years), 1m5w (months+weeks), 30min, 3a (3am today)
91
+
92
+ Times default to local unless TZ specified or -Z used. See --help-dates for
93
+ details.
94
+
95
+ LOGICAL EXPRESSIONS
96
+
97
+ Conditions for --from, --chat, and --service are OR'd with others from same
98
+ option. --links, -Y, -X, -S too are all OR'd together.
99
+
100
+ Everything else is AND'ed by default. Chains of explicit --and/--or combine
101
+ as a single expression. Then get AND'ed with the rest. ANDs bind before ORs.
102
+
103
+ '-f x --or y' is shorthand for '-f x --or -f y'.
104
+
105
+ Mixing options is possible: '-fX -O -c -A -Mfoo'.
106
+
107
+ PATTERNS
108
+
109
+ PATTERN or CONTACT is a regular expression.
110
+
111
+ CONTACT matches address book information: You can give part of a name, phone
112
+ number, email. It doesn't matter which one a message was sent/received from.
113
+
114
+ Smart-case is the default case, which means case-insensitive, unless
115
+ uppercase letters present. Use -q to match literal strings, -i and -I to
116
+ change case sensitivity.
117
+
118
+ The flags -q, -x, -i, -I, and -C affect all subsequent patterns.
119
+ -e and -v affect the immediate next pattern only.
120
+
121
+ CAPTURE
122
+
123
+ EXPR is a format string for the message output where you can use capture
124
+ groups (alongside any text).
125
+
126
+ - When EXPR omitted, the entire match is presented. ('-o' = '-o"\0"')
127
+ - When it's a digit, that capture group is presented. ('-o1' = '-o"\1"')
128
+ - A comma-separated list of digits outputs those groups joined by space.
129
+ - When it's a format string: "foo \1 bar \2", interpolates the captures.
130
+
131
+ For a pattern to be available for capture, it must not be part of a logical
132
+ expression (--and/or) nor negated (-v).
133
+
134
+ If multiple message patterns are given, the last is used for capture.
135
+
136
+ FILES AND IMAGES
137
+
138
+ Inline image previews are possible on terminals that support it. We implement
139
+ iTerm and Kitty (Ghostty, etc) protocols.
140
+
141
+ Previews are available for links and image files. You can preview both (-p)
142
+ or either (-pi, -pl).
143
+
144
+ Filter files by:
145
+ --files # messages having any files at all
146
+ --files=images # with images
147
+ --files=ext:'jpe?g|png' # with jpeg, jpg, png extensions
148
+ --files=mime:video # video files
149
+ --files=name:'\bcats?\b' # files with word 'cat' or 'cats' in the name
150
+
151
+ JSON output does not include files by default. Use '-b' to include.
152
+
153
+ OTHER NOTES
154
+
155
+ '--to foo' will find a message sent by someone else to a group where foo
156
+ is a member. Use '--sent' will constrain to only messages you sent, and '-vc'
157
+ will exclude group chats entirely.
158
+
159
+ When listing links, using '--urls' is advisable so you get a shorter,
160
+ resolved (often canonical) URL.
161
+
162
+ Most things can be abbreviated, some combined:
163
+ --rev = --reverse
164
+ -Vi,r = --service=imessage --or --service=rcs
165
+ -Fn:bar,i = --files=name:bar --or --files=images
166
+
167
+ You cannot combine patterns by comma, but you can use '|': --from 'foo|bar'
168
+ This is generally simpler than using '--or' for same option.
169
+
170
+ Most things are regular expressions, even where you don't expect it.
171
+ Cf. file filter above.
172
+
173
+ EXAMPLES
174
+
175
+ PROG pattern # Message contents search
176
+ PROG -f Alice pattern # From Alice
177
+ PROG -d 2024-01-01 pattern # Since date
178
+ PROG -o '1,2' '(\w+):(\d+)' # Capture groups
179
+ PROG -eqIf andy@example.com -xit 'pedro|bob' -c
180
+ matches case-sensitive, literal, exact (whole email) andy@example.com
181
+ sender, and case-insensitive regexp recipient, and must be a group chat.
data/doc/HELP_DATES ADDED
@@ -0,0 +1,72 @@
1
+ Date and Time Format Reference
2
+
3
+ This tool accepts flexible date and time inputs for filtering messages.
4
+
5
+ ABSOLUTE DATES
6
+ 2024-01-01 Today's date in YYYY-MM-DD format
7
+ 2024-1-1 Relaxed format (single digits OK)
8
+ 2024-12-31 Any valid date
9
+
10
+ DATES WITH TIME
11
+ 2024-01-01 10:30 Date with time (inherits your timezone, unless -Z)
12
+ 2024-01-01T10:30 ISO format with T separator
13
+ 2024-01-01 10:30:45 Include seconds
14
+ 2024-01-01T10:30Z UTC timezone (Z suffix)
15
+ 2024-01-01T10:30+05 Explicit timezone (short)
16
+ 2024-01-01T10:30+05:30 Explicit timezone offset
17
+ 2024-01-01T10:30-08:00 Negative timezone offset
18
+
19
+ TIME ONLY (uses today's date, respecting -Z)
20
+ 1:34 24-hour format
21
+ 23:59 24-hour format
22
+ 10a, 10am 10 AM
23
+ 10p, 10pm 10 PM
24
+ 12:34a 12:34 AM (midnight hour)
25
+ 12:45p 12:45 PM (noon hour)
26
+ 0:34a 12:34 AM
27
+ 1a 1 AM
28
+ 1p 1 PM
29
+
30
+ RELATIVE DATES (how far back)
31
+ 1d 1 day ago
32
+ 7d 7 days ago
33
+ 1w 1 week ago
34
+ 2w 2 weeks ago
35
+ 1m 1 month ago
36
+ 6m 6 months ago
37
+ 1y 1 year ago
38
+ 2y 2 years ago
39
+
40
+ COMBINE RELATIVE DATES
41
+ 1y6m 1 year 6 months ago
42
+ 1y6m2w3d 1 year 6 months 2 weeks 3 days ago
43
+ 6m1w 6 months 1 week ago
44
+
45
+ RELATIVE TIME
46
+ 1h 1 hour ago
47
+ 30M 30 minutes ago (capital M for minutes)
48
+ 30min 30 minutes ago (alternative)
49
+ 45s 45 seconds ago
50
+ 2h30M 2 hours 30 minutes ago
51
+ 1h30M45s 1 hour 30 minutes 45 seconds ago
52
+
53
+ COMBINE DATE AND TIME
54
+ 3d2h 3 days 2 hours ago
55
+ 1d12h30M 1 day 12 hours 30 minutes ago
56
+ 5d3h15M30s 5 days 3 hours 15 minutes 30 seconds ago
57
+ 1min2d 2 days 1 minute ago (order doesn't matter)
58
+ 1d1d2m4m 6 months, 2 days ago (additive)
59
+
60
+ NOTES
61
+ - Optional minus sign: -1d same as 1d
62
+ - Case insensitive: 10AM, 10am, 10A all work
63
+ - M = minutes, m = months in relative times
64
+ - 12am = midnight, 12pm = noon
65
+ - Invalid dates will show an error
66
+
67
+ EXAMPLES
68
+ --since 2024-01-01 Messages after January 1, 2024
69
+ --until 3d Messages before 3 days ago
70
+ --since 1y6m Messages after 1 year 6 months ago
71
+ --until 10:30p Messages before 10:30 PM today
72
+ --since "2d 3h" Messages after 2 days 3 hours ago
data/ext/extconf.rb ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "mkmf"
4
+
5
+ # Check if dylib already exists
6
+ dylib_path = File.join *%W[ #{File.dirname __FILE__} .. lib imsg-grep images img2png.dylib ]
7
+ if File.exist?(dylib_path)
8
+ # Create no-op Makefile since dylib already exists
9
+ File.open("Makefile", "w") do |f|
10
+ f.puts <<~MAKEFILE
11
+ SHELL = /bin/sh
12
+
13
+ all:
14
+ \t@echo "dylib already exists, skipping build"
15
+
16
+ install:
17
+ \t@echo "dylib already exists, skipping install"
18
+
19
+ clean:
20
+
21
+ distclean:
22
+ \trm -f Makefile
23
+
24
+ .PHONY: all install clean distclean
25
+ MAKEFILE
26
+ end
27
+ exit 0
28
+ end
29
+
30
+ # Check for explicit disable via environment variable
31
+ images_disabled = %w[0 no false].include?(ENV['IMSGGREP_IMAGES']&.downcase)
32
+
33
+ # Check build requirements
34
+
35
+ is_darwin = RUBY_PLATFORM.include?("darwin")
36
+ found_swiftc = find_executable("swiftc")
37
+
38
+ has_requirements = is_darwin && found_swiftc
39
+
40
+ if has_requirements
41
+ # Create Swift compilation Makefile
42
+ File.open("Makefile", "w") do |f|
43
+ f.puts <<~MAKEFILE
44
+ SHELL = /bin/sh
45
+
46
+ DLLIB = img2png.dylib
47
+
48
+ all: $(DLLIB)
49
+
50
+ $(DLLIB): img2png.swift
51
+ \tswiftc -O -whole-module-optimization -lto=llvm-full -emit-library -D LIBRARY -o $(DLLIB) img2png.swift
52
+
53
+ install: $(DLLIB)
54
+ \tmkdir -p $(sitearchdir)/imsg-grep/images
55
+ \tcp $(DLLIB) $(sitearchdir)/imsg-grep/images/$(DLLIB)
56
+
57
+ clean:
58
+ \trm -f $(DLLIB)
59
+
60
+ distclean: clean
61
+ \trm -f Makefile
62
+
63
+ .PHONY: all install clean distclean
64
+ MAKEFILE
65
+ end
66
+ elsif images_disabled
67
+ # User explicitly disabled image support
68
+ File.open("Makefile", "w") do |f|
69
+ f.puts <<~MAKEFILE
70
+ SHELL = /bin/sh
71
+
72
+ all:
73
+ \t@echo "Image processing disabled via IMSGGREP_IMAGES environment variable"
74
+
75
+ install:
76
+ \t@echo "Image processing disabled via IMSGGREP_IMAGES environment variable"
77
+
78
+ clean:
79
+
80
+ distclean:
81
+ \trm -f Makefile
82
+
83
+ .PHONY: all install clean distclean
84
+ MAKEFILE
85
+ end
86
+ else
87
+ # Requirements not met - show helpful error and fail
88
+ $stderr.puts "ERROR: img2png extension build requirements not met:"
89
+ $stderr.puts " - Requires macOS (current platform: #{RUBY_PLATFORM})" unless is_darwin
90
+ $stderr.puts " - Requires swiftc (install Xcode or Swift toolchain)" unless found_swiftc
91
+ $stderr.puts ""
92
+ $stderr.puts "To build without image support, set:"
93
+ $stderr.puts " export IMSGGREP_IMAGES=0"
94
+ $stderr.puts ""
95
+
96
+ abort "Build failed due to missing requirements"
97
+ end