twterm 1.1.3 → 1.2.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 +4 -4
- data/bin/twterm +4 -4
- data/lib/twterm/app.rb +19 -4
- data/lib/twterm/client.rb +8 -470
- data/lib/twterm/direct_message.rb +82 -0
- data/lib/twterm/direct_message_composer.rb +74 -0
- data/lib/twterm/direct_message_manager.rb +52 -0
- data/lib/twterm/event/base.rb +22 -0
- data/lib/twterm/event/direct_message/fetched.rb +10 -0
- data/lib/twterm/event/favorite.rb +18 -0
- data/lib/twterm/event/follow.rb +17 -0
- data/lib/twterm/event/notification.rb +33 -0
- data/lib/twterm/event/open_uri.rb +11 -0
- data/lib/twterm/event/screen/resize.rb +13 -0
- data/lib/twterm/event/status/base.rb +14 -0
- data/lib/twterm/event/status/delete.rb +13 -0
- data/lib/twterm/event/status/mention.rb +10 -0
- data/lib/twterm/event/status/timeline.rb +10 -0
- data/lib/twterm/event_dispatcher.rb +59 -0
- data/lib/twterm/filter_query_window.rb +11 -5
- data/lib/twterm/filterable_list.rb +6 -1
- data/lib/twterm/notifier.rb +39 -15
- data/lib/twterm/promise.rb +2 -2
- data/lib/twterm/publisher.rb +16 -0
- data/lib/twterm/rest_client.rb +401 -0
- data/lib/twterm/screen.rb +16 -13
- data/lib/twterm/status.rb +12 -1
- data/lib/twterm/streaming_client.rb +103 -0
- data/lib/twterm/subscriber.rb +33 -0
- data/lib/twterm/tab/base.rb +13 -6
- data/lib/twterm/tab/direct_message/conversation.rb +103 -0
- data/lib/twterm/tab/direct_message/conversation_list.rb +99 -0
- data/lib/twterm/tab/key_assignments_cheatsheet.rb +3 -2
- data/lib/twterm/tab/new/list.rb +5 -3
- data/lib/twterm/tab/new/search.rb +3 -2
- data/lib/twterm/tab/new/start.rb +17 -2
- data/lib/twterm/tab/new/user.rb +6 -3
- data/lib/twterm/tab/statuses/base.rb +18 -11
- data/lib/twterm/tab/statuses/conversation.rb +3 -2
- data/lib/twterm/tab/statuses/favorites.rb +3 -2
- data/lib/twterm/tab/statuses/home.rb +10 -4
- data/lib/twterm/tab/statuses/list_timeline.rb +3 -2
- data/lib/twterm/tab/statuses/mentions.rb +6 -6
- data/lib/twterm/tab/statuses/search.rb +4 -3
- data/lib/twterm/tab/statuses/user_timeline.rb +3 -2
- data/lib/twterm/tab/user_tab.rb +26 -16
- data/lib/twterm/tab/users/base.rb +3 -2
- data/lib/twterm/tab/users/followers.rb +3 -2
- data/lib/twterm/tab/users/friends.rb +3 -2
- data/lib/twterm/tab_manager.rb +20 -8
- data/lib/twterm/tweetbox.rb +5 -2
- data/lib/twterm/uri_opener.rb +25 -0
- data/lib/twterm/user.rb +11 -1
- data/lib/twterm/utils.rb +13 -0
- data/lib/twterm/version.rb +1 -1
- data/lib/twterm.rb +0 -3
- data/spec/twterm/event/screen/resize_spec.rb +11 -0
- data/spec/twterm/event_dispatcher_spec.rb +19 -0
- data/twterm.gemspec +1 -1
- metadata +29 -7
- data/lib/twterm/notification/base.rb +0 -24
- data/lib/twterm/notification/error.rb +0 -19
- data/lib/twterm/notification/message.rb +0 -19
@@ -0,0 +1,401 @@
|
|
1
|
+
require 'twterm/direct_message'
|
2
|
+
require 'twterm/direct_message_manager'
|
3
|
+
require 'twterm/publisher'
|
4
|
+
require 'twterm/event/notification'
|
5
|
+
|
6
|
+
module Twterm
|
7
|
+
module RESTClient
|
8
|
+
include Publisher
|
9
|
+
|
10
|
+
CONSUMER_KEY = 'vLNSVFgXclBJQJRZ7VLMxL9lA'.freeze
|
11
|
+
CONSUMER_SECRET = 'OFLKzrepRG2p1hq0nUB9j2S9ndFQoNTPheTpmOY0GYw55jGgS5'.freeze
|
12
|
+
|
13
|
+
def block(*user_ids)
|
14
|
+
send_request do
|
15
|
+
rest_client.block(*user_ids)
|
16
|
+
end.then do |users|
|
17
|
+
users.each do |user|
|
18
|
+
Friendship.block(self.user_id, user.id)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_direct_message(recipient, text)
|
24
|
+
send_request do
|
25
|
+
rest_client.create_direct_message(recipient.id, text)
|
26
|
+
end.then do |message|
|
27
|
+
msg = DirectMessage.new(message)
|
28
|
+
direct_message_manager.add(msg.recipient, msg)
|
29
|
+
publish(Event::DirectMessage::Fetched.new)
|
30
|
+
publish(Event::Notification.new(:message, 'Your message to @%s has been sent' % msg.recipient.screen_name))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def direct_message_conversations
|
35
|
+
direct_message_manager.conversations
|
36
|
+
end
|
37
|
+
|
38
|
+
def direct_messages_received
|
39
|
+
send_request do
|
40
|
+
rest_client.direct_messages(count: 200).map(&DirectMessage.method(:new))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def direct_messages_sent
|
45
|
+
send_request do
|
46
|
+
rest_client.direct_messages_sent(count: 200).map(&DirectMessage.method(:new))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def destroy_status(status)
|
51
|
+
send_request_without_catch do
|
52
|
+
rest_client.destroy_status(status.id)
|
53
|
+
publish(Event::Status::Delete.new(status.id))
|
54
|
+
publish(Event::Notification.new(:message, 'Your tweet has been deleted'))
|
55
|
+
end.catch do |reason|
|
56
|
+
case reason
|
57
|
+
when Twitter::Error::NotFound, Twitter::Error::Forbidden
|
58
|
+
publish(Event::Notification.new(:error, 'You cannot destroy that status'))
|
59
|
+
else
|
60
|
+
raise reason
|
61
|
+
end
|
62
|
+
end.catch(&show_error)
|
63
|
+
end
|
64
|
+
|
65
|
+
def favorite(status)
|
66
|
+
return false unless status.is_a? Status
|
67
|
+
|
68
|
+
send_request do
|
69
|
+
rest_client.favorite(status.id)
|
70
|
+
end.then do
|
71
|
+
status.favorite!
|
72
|
+
publish(Event::Notification.new(:message, 'Successfully liked: @%s "%s"' % [
|
73
|
+
status.user.screen_name, status.text
|
74
|
+
]))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def favorites(user_id = nil)
|
79
|
+
user_id ||= self.user_id
|
80
|
+
|
81
|
+
send_request do
|
82
|
+
rest_client.favorites(user_id, count: 200)
|
83
|
+
end.then do |tweets|
|
84
|
+
tweets.map(&Status.method(:new))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def fetch_muted_users
|
89
|
+
send_request do
|
90
|
+
@muted_user_ids = rest_client.muted_ids.to_a
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def follow(*user_ids)
|
95
|
+
send_request do
|
96
|
+
rest_client.follow(*user_ids)
|
97
|
+
end.then do |users|
|
98
|
+
users.each do |user|
|
99
|
+
if user.protected?
|
100
|
+
Friendship.following_requested(self.user_id, user.id)
|
101
|
+
else
|
102
|
+
Friendship.follow(self.user_id, user.id)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def followers(user_id = nil)
|
109
|
+
user_id ||= self.user_id
|
110
|
+
|
111
|
+
m = Mutex.new
|
112
|
+
|
113
|
+
send_request do
|
114
|
+
rest_client.follower_ids(user_id).each_slice(100) do |user_ids|
|
115
|
+
m.synchronize do
|
116
|
+
users = rest_client.users(*user_ids).map(& -> u { User.new(u) })
|
117
|
+
users.each do |user|
|
118
|
+
Friendship.follow(user.id, self.user_id)
|
119
|
+
end if user_id == self.user_id
|
120
|
+
yield users
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def friends(user_id = nil)
|
127
|
+
user_id ||= self.user_id
|
128
|
+
|
129
|
+
m = Mutex.new
|
130
|
+
|
131
|
+
send_request do
|
132
|
+
rest_client.friend_ids(user_id).each_slice(100) do |user_ids|
|
133
|
+
m.synchronize do
|
134
|
+
yield rest_client.users(*user_ids).map(& -> u { User.new(u) })
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def home_timeline
|
141
|
+
send_request do
|
142
|
+
rest_client.home_timeline(count: 200)
|
143
|
+
end.then do |statuses|
|
144
|
+
statuses
|
145
|
+
.select(&@mute_filter)
|
146
|
+
.map(&Status.method(:new))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def list(list_id)
|
151
|
+
send_request do
|
152
|
+
rest_client.list(list_id)
|
153
|
+
end.then do |list|
|
154
|
+
List.new(list)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def list_timeline(list)
|
159
|
+
fail ArgumentError,
|
160
|
+
'argument must be an instance of List class' unless list.is_a? List
|
161
|
+
send_request do
|
162
|
+
rest_client.list_timeline(list.id, count: 200)
|
163
|
+
end.then do |statuses|
|
164
|
+
statuses
|
165
|
+
.select(&@mute_filter)
|
166
|
+
.map(&Status.method(:new))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def lists
|
171
|
+
send_request do
|
172
|
+
rest_client.lists
|
173
|
+
end.then do |lists|
|
174
|
+
lists.map { |list| List.new(list) }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def lookup_friendships
|
179
|
+
user_ids = User.ids.reject { |id| Friendship.already_looked_up?(id) }
|
180
|
+
send_request_without_catch do
|
181
|
+
user_ids.each_slice(100) do |chunked_user_ids|
|
182
|
+
friendships = rest_client.friendships(*chunked_user_ids)
|
183
|
+
friendships.each do |friendship|
|
184
|
+
id = friendship.id
|
185
|
+
client_id = user_id
|
186
|
+
|
187
|
+
conn = friendship.connections
|
188
|
+
conn.include?('blocking') ? Friendship.block(client_id, id) : Friendship.unblock(client_id, id)
|
189
|
+
conn.include?('following') ? Friendship.follow(client_id, id) : Friendship.unfollow(client_id, id)
|
190
|
+
conn.include?('following_requested') ? Friendship.following_requested(client_id, id) : Friendship.following_not_requested(client_id, id)
|
191
|
+
conn.include?('followed_by') ? Friendship.follow(id, client_id) : Friendship.unfollow(id, client_id)
|
192
|
+
conn.include?('muting') ? Friendship.mute(client_id, id) : Friendship.unmute(client_id, id)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end.catch do |e|
|
196
|
+
case e
|
197
|
+
when Twitter::Error::TooManyRequests
|
198
|
+
# do nothing
|
199
|
+
else
|
200
|
+
raise e
|
201
|
+
end
|
202
|
+
end.catch(&show_error)
|
203
|
+
end
|
204
|
+
|
205
|
+
def mentions
|
206
|
+
send_request do
|
207
|
+
rest_client.mentions(count: 200)
|
208
|
+
end.then do |statuses|
|
209
|
+
statuses
|
210
|
+
.select(&@mute_filter)
|
211
|
+
.map(&Status.method(:new))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def mute(user_ids)
|
216
|
+
send_request do
|
217
|
+
rest_client.mute(*user_ids)
|
218
|
+
end.then do |users|
|
219
|
+
users.each do |user|
|
220
|
+
Friendship.mute(self.user_id, user.id)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def post(text, in_reply_to = nil)
|
226
|
+
send_request do
|
227
|
+
if in_reply_to.is_a? Status
|
228
|
+
text = "@#{in_reply_to.user.screen_name} #{text}"
|
229
|
+
rest_client.update(text, in_reply_to_status_id: in_reply_to.id)
|
230
|
+
else
|
231
|
+
rest_client.update(text)
|
232
|
+
end
|
233
|
+
publish(Event::Notification.new(:message, 'Your tweet has been posted'))
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def retweet(status)
|
238
|
+
fail ArgumentError,
|
239
|
+
'argument must be an instance of Status class' unless status.is_a? Status
|
240
|
+
|
241
|
+
send_request_without_catch do
|
242
|
+
rest_client.retweet!(status.id)
|
243
|
+
end.then do
|
244
|
+
status.retweet!
|
245
|
+
publish(Event::Notification.new(:message, 'Successfully retweeted: @%s "%s"' % [
|
246
|
+
status.user.screen_name, status.text
|
247
|
+
]))
|
248
|
+
end.catch do |reason|
|
249
|
+
message =
|
250
|
+
case reason
|
251
|
+
when Twitter::Error::AlreadyRetweeted
|
252
|
+
'The status is already retweeted'
|
253
|
+
when Twitter::Error::NotFound
|
254
|
+
'The status is not found'
|
255
|
+
when Twitter::Error::Forbidden
|
256
|
+
if status.user.id == user_id # when the status is mine
|
257
|
+
'You cannot retweet your own status'
|
258
|
+
else # when the status is not mine
|
259
|
+
'The status is protected'
|
260
|
+
end
|
261
|
+
else
|
262
|
+
raise e
|
263
|
+
end
|
264
|
+
publish(Event::Notification.new(:error, "Retweet attempt failed: #{message}"))
|
265
|
+
end.catch(&show_error)
|
266
|
+
end
|
267
|
+
|
268
|
+
def saved_search
|
269
|
+
send_request do
|
270
|
+
rest_client.saved_searches
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def search(query)
|
275
|
+
send_request do
|
276
|
+
rest_client.search(query, count: 100).attrs[:statuses]
|
277
|
+
end.then do |statuses|
|
278
|
+
statuses
|
279
|
+
.map(&Twitter::Tweet.method(:new))
|
280
|
+
.map(&Status.method(:new))
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def show_status(status_id)
|
285
|
+
send_request do
|
286
|
+
rest_client.status(status_id)
|
287
|
+
end.then do |status|
|
288
|
+
Status.new(status)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def show_user(query)
|
293
|
+
send_request_without_catch do
|
294
|
+
rest_client.user(query)
|
295
|
+
end.catch do |reason|
|
296
|
+
case reason
|
297
|
+
when Twitter::Error::NotFound
|
298
|
+
nil
|
299
|
+
else
|
300
|
+
raise reason
|
301
|
+
end
|
302
|
+
end.catch(&show_error).then do |user|
|
303
|
+
user.nil? ? nil : User.new(user)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def unblock(*user_ids)
|
308
|
+
send_request do
|
309
|
+
rest_client.unblock(*user_ids)
|
310
|
+
end.then do |users|
|
311
|
+
users.each do |user|
|
312
|
+
Friendship.unblock(self.user_id, user.id)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def unfavorite(status)
|
318
|
+
fail ArgumentError,
|
319
|
+
'argument must be an instance of Status class' unless status.is_a? Status
|
320
|
+
|
321
|
+
send_request do
|
322
|
+
rest_client.unfavorite(status.id)
|
323
|
+
end.then do
|
324
|
+
status.unfavorite!
|
325
|
+
publish(Event::Notification.new(:message, 'Successfully unliked: @%s "%s"' % [
|
326
|
+
status.user.screen_name, status.text
|
327
|
+
]))
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def unfollow(*user_ids)
|
332
|
+
send_request do
|
333
|
+
rest_client.unfollow(*user_ids)
|
334
|
+
end.then do |users|
|
335
|
+
users.each do |user|
|
336
|
+
Friendship.unfollow(self.user_id, user.id)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def unmute(user_ids)
|
342
|
+
send_request do
|
343
|
+
rest_client.unmute(*user_ids)
|
344
|
+
end.then do |users|
|
345
|
+
users.each do |user|
|
346
|
+
Friendship.unmute(self.user_id, user.id)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def user_timeline(user_id)
|
352
|
+
send_request do
|
353
|
+
rest_client.user_timeline(user_id, count: 200)
|
354
|
+
end.then do |statuses|
|
355
|
+
statuses
|
356
|
+
.select(&@mute_filter)
|
357
|
+
.map(&Status.method(:new))
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def rest_client
|
362
|
+
@rest_client ||= Twitter::REST::Client.new do |config|
|
363
|
+
config.consumer_key = CONSUMER_KEY
|
364
|
+
config.consumer_secret = CONSUMER_SECRET
|
365
|
+
config.access_token = @access_token
|
366
|
+
config.access_token_secret = @access_token_secret
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def send_request(&block)
|
371
|
+
send_request_without_catch(&block).catch(&show_error)
|
372
|
+
end
|
373
|
+
|
374
|
+
def send_request_without_catch(&block)
|
375
|
+
Promise.new do |resolve, reject|
|
376
|
+
begin
|
377
|
+
resolve.(block.call)
|
378
|
+
rescue Twitter::Error => reason
|
379
|
+
reject.(reason)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
private
|
385
|
+
|
386
|
+
def direct_message_manager
|
387
|
+
@direct_message_manager ||= DirectMessageManager.new(self)
|
388
|
+
end
|
389
|
+
|
390
|
+
def show_error
|
391
|
+
proc do |e|
|
392
|
+
case e
|
393
|
+
when Twitter::Error
|
394
|
+
publish(Event::Notification.new(:error, "Failed to send request: #{e.message}"))
|
395
|
+
else
|
396
|
+
raise e
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
data/lib/twterm/screen.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
require 'twterm/event/screen/resize'
|
2
|
+
require 'twterm/subscriber'
|
3
|
+
|
1
4
|
module Twterm
|
2
5
|
class Screen
|
3
6
|
include Singleton
|
7
|
+
include Subscriber
|
4
8
|
include Curses
|
5
9
|
|
6
10
|
def initialize
|
@@ -11,6 +15,8 @@ module Twterm
|
|
11
15
|
stdscr.keypad(true)
|
12
16
|
start_color
|
13
17
|
use_default_colors
|
18
|
+
|
19
|
+
subscribe(Event::Screen::Resize, :resize)
|
14
20
|
end
|
15
21
|
|
16
22
|
def refresh
|
@@ -19,19 +25,6 @@ module Twterm
|
|
19
25
|
Notifier.instance.show
|
20
26
|
end
|
21
27
|
|
22
|
-
def resize
|
23
|
-
return if closed?
|
24
|
-
|
25
|
-
resizeterm(`tput lines`.to_i, `tput cols`.to_i)
|
26
|
-
@screen.resize(`tput lines`.to_i, `tput cols`.to_i)
|
27
|
-
|
28
|
-
TabManager.instance.resize
|
29
|
-
TabManager.instance.each_tab(&:resize)
|
30
|
-
Notifier.instance.resize
|
31
|
-
FilterQueryWindow.instance.resize
|
32
|
-
refresh
|
33
|
-
end
|
34
|
-
|
35
28
|
def respond_to_key(key)
|
36
29
|
case key
|
37
30
|
when ?n
|
@@ -58,6 +51,16 @@ module Twterm
|
|
58
51
|
|
59
52
|
private
|
60
53
|
|
54
|
+
def resize(event)
|
55
|
+
return if closed?
|
56
|
+
|
57
|
+
lines, cols = event.lines, event.cols
|
58
|
+
resizeterm(lines, cols)
|
59
|
+
@screen.resize(lines, cols)
|
60
|
+
|
61
|
+
refresh
|
62
|
+
end
|
63
|
+
|
61
64
|
def scan
|
62
65
|
App.instance.reset_interruption_handler
|
63
66
|
|
data/lib/twterm/status.rb
CHANGED
@@ -113,10 +113,15 @@ module Twterm
|
|
113
113
|
end
|
114
114
|
|
115
115
|
def update!(tweet)
|
116
|
+
return self if recently_updated?
|
117
|
+
|
116
118
|
@retweet_count = tweet.retweet_count
|
117
119
|
@favorite_count = tweet.favorite_count
|
118
120
|
@retweeted = tweet.retweeted?
|
119
121
|
@favorited = tweet.favorited?
|
122
|
+
|
123
|
+
@updated_at = Time.now
|
124
|
+
|
120
125
|
self
|
121
126
|
end
|
122
127
|
|
@@ -135,7 +140,7 @@ module Twterm
|
|
135
140
|
cond = -> (status) { status.touched_at > Time.now - MAX_CACHED_TIME }
|
136
141
|
statuses = all.select(&cond)
|
137
142
|
status_ids = statuses.map(&:id)
|
138
|
-
@@instances = status_ids.zip(statuses)
|
143
|
+
@@instances = Hash[status_ids.zip(statuses)]
|
139
144
|
end
|
140
145
|
|
141
146
|
def self.delete(id)
|
@@ -159,5 +164,11 @@ module Twterm
|
|
159
164
|
instance = find(tweet.id)
|
160
165
|
instance.nil? ? super : instance.update!(tweet)
|
161
166
|
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def recently_updated?
|
171
|
+
!@updated_at.nil? && @updated_at + 60 > Time.now
|
172
|
+
end
|
162
173
|
end
|
163
174
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'twterm/event/favorite'
|
2
|
+
require 'twterm/event/notification'
|
3
|
+
require 'twterm/event/status/mention'
|
4
|
+
require 'twterm/event/status/timeline'
|
5
|
+
require 'twterm/publisher'
|
6
|
+
|
7
|
+
module Twterm
|
8
|
+
module StreamingClient
|
9
|
+
include Publisher
|
10
|
+
|
11
|
+
CONSUMER_KEY = 'vLNSVFgXclBJQJRZ7VLMxL9lA'.freeze
|
12
|
+
CONSUMER_SECRET = 'OFLKzrepRG2p1hq0nUB9j2S9ndFQoNTPheTpmOY0GYw55jGgS5'.freeze
|
13
|
+
|
14
|
+
def connect_user_stream
|
15
|
+
streaming_client.stop_stream
|
16
|
+
|
17
|
+
@streaming_thread = Thread.new do
|
18
|
+
begin
|
19
|
+
publish(Event::Notification.new(:message, 'Trying to connect to Twitter...'))
|
20
|
+
streaming_client.userstream
|
21
|
+
rescue EventMachine::ConnectionError
|
22
|
+
publish(Event::Notification.new(:error, 'Connection failed'))
|
23
|
+
sleep 30
|
24
|
+
retry
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize_user_stream
|
30
|
+
return if user_stream_initialized?
|
31
|
+
|
32
|
+
streaming_client.on_friends do
|
33
|
+
user_stream_connected!
|
34
|
+
end
|
35
|
+
|
36
|
+
streaming_client.on_timeline_status do |tweet|
|
37
|
+
status = Status.new(tweet)
|
38
|
+
publish(Event::Status::Timeline.new(status))
|
39
|
+
publish(Event::Status::Mention.new(status)) if status.text.include?('@%s' % screen_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
streaming_client.on_delete do |status_id|
|
43
|
+
publish(Event::StatusDeleted.new(status_id))
|
44
|
+
end
|
45
|
+
|
46
|
+
streaming_client.on_event(:favorite) do |event|
|
47
|
+
user = User.new(Twitter::User.new(event[:source]))
|
48
|
+
status = Status.new(Twitter::Status.new(event[:target]))
|
49
|
+
|
50
|
+
event = Event::Favorite.new(user, status, self)
|
51
|
+
publish(event)
|
52
|
+
end
|
53
|
+
|
54
|
+
streaming_client.on_event(:follow) do |event|
|
55
|
+
source = User.new(Twitter::User.new(event[:source]))
|
56
|
+
target = User.new(Twitter::User.new(event[:target]))
|
57
|
+
|
58
|
+
event = Event::Follow.new(source, target, self)
|
59
|
+
publish(:followed, event)
|
60
|
+
end
|
61
|
+
|
62
|
+
streaming_client.on_no_data_received do
|
63
|
+
user_stream_disconnected!
|
64
|
+
connect_user_stream
|
65
|
+
end
|
66
|
+
|
67
|
+
user_stream_initialized!
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def streaming_client
|
73
|
+
@streaming_client ||= TweetStream::Client.new(
|
74
|
+
consumer_key: CONSUMER_KEY,
|
75
|
+
consumer_secret: CONSUMER_SECRET,
|
76
|
+
oauth_token: @access_token,
|
77
|
+
oauth_token_secret: @access_token_secret,
|
78
|
+
auth_method: :oauth
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def user_stream_connected?
|
83
|
+
@user_stream_connected || false
|
84
|
+
end
|
85
|
+
|
86
|
+
def user_stream_connected!
|
87
|
+
publish(Event::Notification.new(:message, 'Connection established')) unless user_stream_connected?
|
88
|
+
@user_stream_connected = true
|
89
|
+
end
|
90
|
+
|
91
|
+
def user_stream_disconnected!
|
92
|
+
@user_stream_connected = false
|
93
|
+
end
|
94
|
+
|
95
|
+
def user_stream_initialized?
|
96
|
+
@user_stream_initialized || false
|
97
|
+
end
|
98
|
+
|
99
|
+
def user_stream_initialized!
|
100
|
+
@user_stream_initialized = true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'twterm/event_dispatcher'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Subscriber
|
5
|
+
def subscribe(event, callback = nil, &block)
|
6
|
+
cb = if callback.is_a?(Proc)
|
7
|
+
callback
|
8
|
+
elsif callback.is_a?(Symbol)
|
9
|
+
if self.respond_to?(callback, true)
|
10
|
+
self.method(callback)
|
11
|
+
else
|
12
|
+
callback.to_proc
|
13
|
+
end
|
14
|
+
elsif callback.nil?
|
15
|
+
block
|
16
|
+
end
|
17
|
+
|
18
|
+
EventDispatcher.instance.register_subscription(object_id, event, cb)
|
19
|
+
end
|
20
|
+
|
21
|
+
def unsubscribe(event = nil)
|
22
|
+
EventDispatcher.instance.unregister_subscription(object_id, event)
|
23
|
+
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.included(base)
|
28
|
+
base.instance_eval do
|
29
|
+
private :subscribe, :unsubscribe
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/twterm/tab/base.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
require 'twterm/event/screen/resize'
|
2
|
+
require 'twterm/subscriber'
|
3
|
+
|
1
4
|
module Twterm
|
2
5
|
module Tab
|
3
|
-
|
6
|
+
class Base
|
4
7
|
include Curses
|
8
|
+
include Subscriber
|
5
9
|
|
6
10
|
attr_reader :window
|
7
11
|
attr_accessor :title
|
@@ -11,11 +15,14 @@ module Twterm
|
|
11
15
|
end
|
12
16
|
|
13
17
|
def close
|
18
|
+
unsubscribe
|
14
19
|
window.close
|
15
20
|
end
|
16
21
|
|
17
22
|
def initialize
|
18
23
|
@window = stdscr.subwin(stdscr.maxy - 5, stdscr.maxx, 3, 0)
|
24
|
+
|
25
|
+
subscribe(Event::Screen::Resize, :resize)
|
19
26
|
end
|
20
27
|
|
21
28
|
def refresh
|
@@ -37,11 +44,6 @@ module Twterm
|
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
40
|
-
def resize
|
41
|
-
window.resize(stdscr.maxy - 5, stdscr.maxx)
|
42
|
-
window.move(3, 0)
|
43
|
-
end
|
44
|
-
|
45
47
|
def respond_to_key(_)
|
46
48
|
fail NotImplementedError, 'respond_to_key method must be implemented'
|
47
49
|
end
|
@@ -65,6 +67,11 @@ module Twterm
|
|
65
67
|
)
|
66
68
|
end
|
67
69
|
|
70
|
+
def resize(event)
|
71
|
+
window.resize(stdscr.maxy - 5, stdscr.maxx)
|
72
|
+
window.move(3, 0)
|
73
|
+
end
|
74
|
+
|
68
75
|
def update
|
69
76
|
fail NotImplementedError, 'update method must be implemented'
|
70
77
|
end
|