pidgin2adium 3.0.1 → 3.1.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.
@@ -0,0 +1,97 @@
1
+ # The Message class and its subclasses, each used for holding one line of a
2
+ # chat.
3
+
4
+ module Pidgin2Adium
5
+ # A holding object for each line of the chat. It is subclassed as
6
+ # appropriate (eg AutoReplyMessage). Each subclass (but not Message
7
+ # itself) has its own to_s which prints out its information in a format
8
+ # appropriate for putting in an Adium log file.
9
+ # Subclasses: XMLMessage, AutoReplyMessage, StatusMessage, Event.
10
+ class Message
11
+ def initialize(sender, time, buddy_alias)
12
+ # The sender's screen name
13
+ @sender = sender
14
+ # The time the message was sent, in Adium format (e.g.
15
+ # "2008-10-05T22:26:20-0800")
16
+ @time = time
17
+ # The receiver's alias (NOT screen name)
18
+ @buddy_alias = buddy_alias
19
+ end
20
+ attr_accessor :sender, :time, :buddy_alias
21
+ end
22
+
23
+ # Basic message with body text (as opposed to pure status messages, which
24
+ # have no body).
25
+ class XMLMessage < Message
26
+ def initialize(sender, time, buddy_alias, body)
27
+ super(sender, time, buddy_alias)
28
+ @body = body
29
+ @styled_body = '<div><span style="font-family: Helvetica; font-size: 12pt;">%s</span></div>' % @body
30
+ normalize_body!()
31
+ end
32
+ attr_accessor :body
33
+
34
+ def to_s
35
+ return sprintf('<message sender="%s" time="%s" alias="%s">%s</message>' << "\n",
36
+ @sender, @time, @buddy_alias, @styled_body)
37
+ end
38
+
39
+ # Balances mismatched tags, normalizes body style, and fixes actions
40
+ # so they are in Adium style (Pidgin uses "***Buddy waves at you", Adium uses
41
+ # "*Buddy waves at you*").
42
+ def normalize_body!
43
+ normalize_body_entities!()
44
+ # Fix mismatched tags. Yes, it's faster to do it per-message
45
+ # than all at once.
46
+ @body = Pidgin2Adium.balance_tags_c(@body)
47
+ if @buddy_alias[0,3] == '***'
48
+ # "***<alias>" is what pidgin sets as the alias for a /me action
49
+ @buddy_alias.slice!(0,3)
50
+ @body = '*' << @body << '*'
51
+ end
52
+ end
53
+
54
+ # Escapes entities.
55
+ def normalize_body_entities!
56
+ # Convert '&' to '&amp;' only if it's not followed by an entity.
57
+ @body.gsub!(/&(?!lt|gt|amp|quot|apos)/, '&amp;')
58
+ end
59
+ end # END XMLMessage class
60
+
61
+ # An auto reply message.
62
+ class AutoReplyMessage < XMLMessage
63
+ def to_s
64
+ return sprintf('<message sender="%s" time="%s" auto="true" alias="%s">%s</message>' << "\n",
65
+ @sender, @time, @buddy_alias, @styled_body)
66
+ end
67
+ end # END AutoReplyMessage class
68
+
69
+ # A message saying e.g. "Blahblah has gone away."
70
+ class StatusMessage < Message
71
+ def initialize(sender, time, buddy_alias, status)
72
+ super(sender, time, buddy_alias)
73
+ @status = status
74
+ end
75
+ attr_accessor :status
76
+
77
+ def to_s
78
+ return sprintf('<status type="%s" sender="%s" time="%s" alias="%s"/>' << "\n", @status, @sender, @time, @buddy_alias)
79
+ end
80
+ end # END StatusMessage class
81
+
82
+ # Pidgin does not have Events, but Adium does. Pidgin mostly uses system
83
+ # messages to display what Adium calls events. These include sending a file,
84
+ # starting a Direct IM connection, or an error in chat.
85
+ class Event < XMLMessage
86
+ def initialize(sender, time, buddy_alias, body, event_type)
87
+ super(sender, time, buddy_alias, body)
88
+ @event_type = event_type
89
+ end
90
+ attr_accessor :event_type
91
+
92
+ def to_s
93
+ return sprintf('<event type="%s" sender="%s" time="%s" alias="%s">%s</event>',
94
+ @event_type, @sender, @time, @buddy_alias, @styled_body)
95
+ end
96
+ end # END Event class
97
+ end
@@ -0,0 +1,39 @@
1
+ # TextLogParser class, a subclass of BasicParser.
2
+ # Used for parse()ing text logs.
3
+
4
+ module Pidgin2Adium
5
+ class TextLogParser < BasicParser
6
+ def initialize(src_path, user_aliases)
7
+ super(src_path, user_aliases)
8
+ @timestamp_rx = '\((\d{1,2}:\d{1,2}:\d{1,2})\)'
9
+
10
+ # @line_regex matches a line in a TXT log file other than the first
11
+ # @line_regex matchdata:
12
+ # 0: timestamp
13
+ # 1: screen name or alias, if alias set
14
+ # 2: "<AUTO-REPLY>" or nil
15
+ # 3: message body
16
+ @line_regex = /#{@timestamp_rx} (.*?) ?(<AUTO-REPLY>)?: (.*)/o
17
+ # @line_regex_status matches a status line
18
+ # @line_regex_status matchdata:
19
+ # 0: timestamp
20
+ # 1: status message
21
+ @line_regex_status = /#{@timestamp_rx} ([^:]+)/o
22
+ end
23
+
24
+ def cleanup(text)
25
+ text.tr!("\r", '')
26
+ # Escape entities since this will be in XML
27
+ text.gsub!('&', '&amp;') # escape '&' first
28
+ text.gsub!('<', '&lt;')
29
+ text.gsub!('>', '&gt;')
30
+ text.gsub!('"', '&quot;')
31
+ text.gsub!("'", '&apos;')
32
+ # Replace newlines with "<br/>" unless they end a chat line.
33
+ # Add the <br/> after converting to &lt; etc so we
34
+ # don't escape the tag.
35
+ text.gsub!(/\n(?!(#{@timestamp_rx}|\Z))/, '<br/>')
36
+ return text
37
+ end
38
+ end # END TextLogParser class
39
+ end
data/pidgin2adium.gemspec CHANGED
@@ -5,45 +5,61 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{pidgin2adium}
8
- s.version = "3.0.1"
8
+ s.version = "3.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Gabe Berke-Williams"]
12
- s.date = %q{2010-08-07}
12
+ s.date = %q{2010-08-13}
13
+ s.default_executable = %q{pidgin2adium}
13
14
  s.description = %q{Pidgin2Adium is a fast, easy way to convert Pidgin (formerly gaim) logs to the Adium format.}
14
15
  s.email = %q{gbw@brandeis.edu}
15
- s.executables = ["pidgin2adium", "pidgin2adium_profiler"]
16
+ s.executables = ["pidgin2adium"]
16
17
  s.extensions = ["ext/balance_tags_c/extconf.rb"]
17
18
  s.extra_rdoc_files = [
18
- "LICENSE",
19
+ "ChangeLog",
20
+ "LICENSE",
19
21
  "README.rdoc"
20
22
  ]
21
23
  s.files = [
22
24
  ".autotest",
23
25
  ".gitignore",
24
26
  ".rspec",
27
+ "ChangeLog",
25
28
  "Gemfile",
26
- "History.txt",
27
29
  "LICENSE",
28
30
  "Manifest.txt",
29
31
  "README.rdoc",
30
32
  "Rakefile",
31
33
  "VERSION",
32
34
  "bin/pidgin2adium",
33
- "bin/pidgin2adium_profiler",
34
35
  "config/website.yml",
35
36
  "ext/balance_tags_c/balance_tags_c.c",
36
37
  "ext/balance_tags_c/extconf.rb",
37
38
  "lib/pidgin2adium.rb",
38
39
  "lib/pidgin2adium/balance_tags.rb",
40
+ "lib/pidgin2adium/basic_parser.rb",
41
+ "lib/pidgin2adium/html_log_parser.rb",
39
42
  "lib/pidgin2adium/log_converter.rb",
40
43
  "lib/pidgin2adium/log_file.rb",
41
44
  "lib/pidgin2adium/log_parser.rb",
45
+ "lib/pidgin2adium/message.rb",
46
+ "lib/pidgin2adium/text_log_parser.rb",
42
47
  "pidgin2adium.gemspec",
48
+ "spec/balance_tags_c_extn_spec.rb",
49
+ "spec/basic_parser_spec.rb",
50
+ "spec/html_log_parser_spec.rb",
51
+ "spec/log_converter_spec.rb",
52
+ "spec/log_file_spec.rb",
53
+ "spec/logfiles/2006-12-21.223606.txt",
54
+ "spec/logfiles/2008-01-15.071445-0500PST.htm",
55
+ "spec/logfiles/2008-01-15.071445-0500PST.html",
43
56
  "spec/pidgin2adium_spec.rb",
44
57
  "spec/spec.opts",
45
58
  "spec/spec_helper.rb",
46
- "tasks/build_profiler.rake",
59
+ "spec/test-output/README.md",
60
+ "spec/test-output/html_log_output.xml",
61
+ "spec/test-output/text_log_output.xml",
62
+ "spec/text_log_parser_spec.rb",
47
63
  "tasks/extconf.rake",
48
64
  "tasks/extconf/balance_tags_c.rake"
49
65
  ]
@@ -53,8 +69,14 @@ Gem::Specification.new do |s|
53
69
  s.rubygems_version = %q{1.3.7}
54
70
  s.summary = %q{Pidgin2Adium is a fast, easy way to convert Pidgin (formerly gaim) logs to the Adium format}
55
71
  s.test_files = [
56
- "spec/pidgin2adium_spec.rb",
57
- "spec/spec_helper.rb"
72
+ "spec/balance_tags_c_extn_spec.rb",
73
+ "spec/basic_parser_spec.rb",
74
+ "spec/html_log_parser_spec.rb",
75
+ "spec/log_converter_spec.rb",
76
+ "spec/log_file_spec.rb",
77
+ "spec/pidgin2adium_spec.rb",
78
+ "spec/spec_helper.rb",
79
+ "spec/text_log_parser_spec.rb"
58
80
  ]
59
81
 
60
82
  if s.respond_to? :specification_version then
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'ext', 'balance_tags_c'))
4
+ require "balance_tags_c"
5
+
6
+ describe "BalanceTagsCExtension" do
7
+ describe "text without tags" do
8
+ it "should be left untouched" do
9
+ text = "Foo bar baz, this is my excellent test text!"
10
+ Pidgin2Adium.balance_tags_c(text).should == text
11
+ end
12
+ end
13
+
14
+ describe "text with tags" do
15
+ it "should be balanced correctly" do
16
+ Pidgin2Adium.balance_tags_c('<p><b>this is unbalanced!').should == "<p><b>this is unbalanced!</b></p>"
17
+ end
18
+
19
+ # Make sure it doesn't segfault
20
+ it "should be balanced correctly when run many times" do
21
+ text = <<-LATIN
22
+ Sequi unde et nobis ipsum. Expedita temporibus aut adipisci debitis
23
+ porro ducimus. Dignissimos est tenetur vero error voluptatem quidem
24
+ ducimus. Sapiente non occaecati omnis non provident sint ut. Repellat
25
+ laudantium quis aperiam ad fugit accusantium placeat itaque. Quia
26
+ velit voluptatem sint aliquid rem quam occaecati doloremque. Eos
27
+ provident ut suscipit reprehenderit mollitia. Non vitae voluptatem
28
+ laudantium quis a et. In libero voluptas aliquam.Veniam minima
29
+ consequatur quod. Voluptatem quibusdam ut consequatur et ratione
30
+ repellat. Iusto est aspernatur consequatur ex nostrum voluptas
31
+ voluptas et. Rerum voluptas et veritatis ratione voluptates ut iusto
32
+ ut. Aspernatur sed molestiae sint eveniet asperiores mollitia qui.
33
+ Rerum laudantium architecto soluta. Earum qui ut vel corporis ullam
34
+ doloribus voluptatem. Nemo quo recusandae ut. Deleniti vel ea qui ut
35
+ perferendis. Est dolor ducimus voluptatem nemo quis et animi
36
+ reprehenderit. Laudantium voluptas adipisci alias. Ut aut soluta
37
+ repellat consequuntur quidem. Deserunt voluptatem eum eveniet cum.Quia
38
+ consectetur at ut quisquam occaecati et sint. Sint voluptatem quaerat
39
+ qui molestiae ratione voluptatem. Autem labore quos perferendis enim
40
+ fuga deleniti recusandae. Aut libero quo cum autem voluptatem.
41
+ LATIN
42
+ 2_000.times do
43
+ Pidgin2Adium.balance_tags_c("<p><b>#{text}").should == "<p><b>#{text}</b></p>"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,217 @@
1
+ require 'spec_helper'
2
+
3
+ describe "BasicParser" do
4
+ it "should include Pidgin2Adium" do
5
+ Pidgin2Adium::BasicParser.included_modules.include?(Pidgin2Adium).should be_true
6
+ end
7
+
8
+ describe "#parse" do
9
+ it "should return false" do
10
+ bp = Pidgin2Adium::BasicParser.new(@text_logfile_path,
11
+ @aliases)
12
+ bp.parse().should be_false
13
+ end
14
+ end
15
+
16
+ describe "#get_time_zone_offset" do
17
+ context "with no timezone available" do
18
+ it "should return the local time zone" do
19
+ bp = Pidgin2Adium::BasicParser.new(@text_logfile_path,
20
+ @aliases)
21
+ bp.get_time_zone_offset.should == @current_tz_offset
22
+ end
23
+ end
24
+
25
+ context "with a time zone available" do
26
+ it "should return the logfiles's time zone" do
27
+ bp = Pidgin2Adium::BasicParser.new(@html_logfile_path,
28
+ @aliases)
29
+ bp.get_time_zone_offset.should == "-0500"
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#create_adium_time" do
35
+ before(:each) do
36
+ @first_line_time = "4/18/2007 11:02:00 AM"
37
+ @time = "2007-08-20 12:33:13"
38
+ @minimal_time = "04:22:05 AM"
39
+ @minimal_time_2 = "04:22:05"
40
+ @invalid_time = "Hammer time!"
41
+
42
+ # Use HTML logfile because it has an explicit timezone (-0500), so we
43
+ # don't have to calculate it out.
44
+ @bp = Pidgin2Adium::BasicParser.new(@html_logfile_path,
45
+ @aliases)
46
+ end
47
+
48
+ it "should parse a first line time correctly" do
49
+ time = @bp.create_adium_time(@first_line_time, true)
50
+ time.should == "2007-04-18T11.02.00-0500"
51
+ end
52
+
53
+ it "should parse a normal time correctly" do
54
+ time = @bp.create_adium_time(@time)
55
+ time.should == "2007-08-20T12:33:13-0500"
56
+ end
57
+
58
+ it "should parse a minimal time correctly" do
59
+ time = @bp.create_adium_time(@minimal_time)
60
+ time.should == "2008-01-15T04:22:05-0500"
61
+ end
62
+
63
+ it "should parse a minimal time without AM/PM correctly" do
64
+ time = @bp.create_adium_time(@minimal_time_2)
65
+ time.should == "2008-01-15T04:22:05-0500"
66
+ end
67
+
68
+ it "should return an array of nils for an invalid time" do
69
+ time = @bp.create_adium_time(@invalid_time)
70
+ time.should be_nil
71
+ end
72
+ end
73
+
74
+ describe "#pre_parse" do
75
+ it "should raise an error for an invalid first line" do
76
+ bp = Pidgin2Adium::BasicParser.new(
77
+ File.join(@current_dir,
78
+ "logfiles",
79
+ "invalid-first-line.txt"),
80
+ @aliases)
81
+ lambda do
82
+ bp.pre_parse()
83
+ end.should raise_error(Pidgin2Adium::InvalidFirstLineError)
84
+ end
85
+
86
+ it "should return correct info for an valid first line" do
87
+ bp = Pidgin2Adium::BasicParser.new(@html_logfile_path,
88
+ @aliases)
89
+ results = bp.pre_parse()
90
+ results.should be_instance_of(Array)
91
+ results.should == ['aim', # service
92
+ 'othersn', # my SN
93
+ 'aolsystemmsg', # other person's SN
94
+ {:year=>2008, :mon=>1, :mday=>15}, # basic time info
95
+ '2008-01-15T07.14.45-0500' # chat start time
96
+ ]
97
+ end
98
+ end
99
+
100
+ describe "#get_sender_by_alias" do
101
+ before(:each) do
102
+ @my_alias = "Gabe B-W"
103
+ @my_SN = "awesomesn" # normalized from "awesome SN"
104
+
105
+ @partner_alias = "Leola Farber III"
106
+ @partner_SN = "BUDDY_PERSON" # not normalized
107
+ # Use text logfile since it has aliases set up.
108
+ @bp = Pidgin2Adium::BasicParser.new(@text_logfile_path,
109
+ @my_alias)
110
+ end
111
+
112
+ it "should return my SN when passed my alias" do
113
+ @bp.get_sender_by_alias(@my_alias).should == @my_SN
114
+ end
115
+
116
+ it "should return my SN when passed my alias with an action" do
117
+ @bp.get_sender_by_alias("***#{@my_alias}").should == @my_SN
118
+ end
119
+
120
+ it "should return partner's SN when passed partner's alias" do
121
+ @bp.get_sender_by_alias(@partner_alias).should == @partner_SN
122
+ end
123
+ end
124
+
125
+ describe "#create_msg" do
126
+ before(:each) do
127
+ body = "Your screen name (otherSN) is now signed into " +
128
+ "AOL(R) Instant Messenger (TM) in 2 locations. " +
129
+ "To sign off the other location(s), reply to this message " + "with the number 1. Click " +
130
+ "<a href='http://www.aim.com/password/routing.adp'>here</a> " +
131
+ "for more information."
132
+ @matches = ['2008-01-15T07.14.45-0500', # time
133
+ 'AOL System Msg', # alias
134
+ nil, # not an auto-reply
135
+ body # message body
136
+ ]
137
+ @auto_reply_matches = @matches.dup
138
+ @auto_reply_matches[2] = '<AUTO-REPLY>'
139
+
140
+ @bp = Pidgin2Adium::BasicParser.new(@text_logfile_path,
141
+ "Gabe B-W")
142
+ end
143
+
144
+
145
+ it "should return XMLMessage class for a normal message" do
146
+ @bp.create_msg(@matches).should
147
+ be_instance_of(Pidgin2Adium::XMLMessage)
148
+ end
149
+
150
+ it "should return AutoReplyMessage class for an auto reply" do
151
+ @bp.create_msg(@auto_reply_matches).should
152
+ be_instance_of(Pidgin2Adium::AutoReplyMessage)
153
+ end
154
+
155
+ it "should return nil if the time is nil" do
156
+ @matches[0] = nil
157
+ @bp.create_msg(@matches).should be_nil
158
+ end
159
+ end
160
+
161
+ describe "#create_status_or_event_msg" do
162
+ before(:each) do
163
+ # not yet converted to Adium format
164
+ @time = "2007-08-20 12:33:13"
165
+ @alias = "Gabe B-W"
166
+ @status_map = {
167
+ "#{@alias} logged in." => 'online',
168
+ "#{@alias} logged out." => 'offline',
169
+ "#{@alias} has signed on." => 'online',
170
+ "#{@alias} has signed off." => 'offline',
171
+ "#{@alias} has gone away." => 'away',
172
+ "#{@alias} is no longer away." => 'available',
173
+ "#{@alias} has become idle." => 'idle',
174
+ "#{@alias} is no longer idle." => 'available'
175
+ }
176
+
177
+ # Small subset of all events
178
+ @libpurple_event_msg = "Starting transfer of cute kitties.jpg from Gabe B-W"
179
+ @event_msg = "You missed 8 messages from Gabe B-W because they were too large"
180
+ @event_type = 'chat-error'
181
+
182
+ @ignored_event_msg = "Gabe B-W is now known as gbw.<br/>"
183
+
184
+ @bp = Pidgin2Adium::BasicParser.new(@html_logfile_path,
185
+ @alias)
186
+ end
187
+
188
+ it "should map statuses correctly" do
189
+ @status_map.each do |message, status|
190
+ return_value = @bp.create_status_or_event_msg([@time,
191
+ message])
192
+ return_value.should be_instance_of(Pidgin2Adium::StatusMessage)
193
+ return_value.status.should == status
194
+ end
195
+ end
196
+
197
+ it "should map libpurple events correctly" do
198
+ return_val = @bp.create_status_or_event_msg([@time,
199
+ @libpurple_event_msg])
200
+ return_val.should be_instance_of(Pidgin2Adium::Event)
201
+ return_val.event_type.should == 'libpurpleEvent'
202
+ end
203
+
204
+ it "should map non-libpurple events correctly" do
205
+ return_val = @bp.create_status_or_event_msg([@time,
206
+ @event_msg])
207
+ return_val.should be_instance_of(Pidgin2Adium::Event)
208
+ return_val.event_type.should == @event_type
209
+ end
210
+
211
+ it "should return nil for ignored events" do
212
+ return_val = @bp.create_status_or_event_msg([@time,
213
+ @ignored_event_msg])
214
+ return_val.should be_nil
215
+ end
216
+ end
217
+ end